Czy istnieje „valueof” podobna do „keyof” w TypeScript?


119

Chcę mieć możliwość przypisania właściwości obiektu do wartości podanej jako klucz i wartość jako dane wejściowe, a mimo to nadal być w stanie określić typ wartości. Trochę trudno to wyjaśnić, więc ten kod powinien ujawnić problem:

type JWT = { id: string, token: string, expire: Date };
const obj: JWT = { id: 'abc123', token: 'tk01', expire: new Date(2018, 2, 14) };

function print(key: keyof JWT) {
    switch (key) {
        case 'id':
        case 'token':
            console.log(obj[key].toUpperCase());
            break;
        case 'expire':
            console.log(obj[key].toISOString());
            break;
    }
}

function onChange(key: keyof JWT, value: any) {
    switch (key) {
        case 'id':
        case 'token':
            obj[key] = value + ' (assigned)';
            break;
        case 'expire':
            obj[key] = value;
            break;
    }
}

print('id');
print('expire');
onChange('id', 'def456');
onChange('expire', new Date(2018, 3, 14));
print('id');
print('expire');

onChange('expire', 1337); // should fail here at compile time
print('expire'); // actually fails here at run time

Próbowałem zmienia value: anysię value: valueof JWT, ale to nie działa.

Najlepiej onChange('expire', 1337)byłoby , gdyby 1337nie był typem daty.

Jak mogę zmienić value: anywartość podanego klucza?

Odpowiedzi:


223

AKTUALIZACJA: Wygląda na to, że tytuł pytania przyciąga ludzi szukających unii wszystkich możliwych typów wartości nieruchomości, analogicznie do sposobu, w jaki keyofdaje sumę wszystkich możliwych typów kluczy właściwości. Najpierw pomóżmy tym ludziom. Możesz zrobić ValueOfanalogię do keyof, używając typów wyszukiwania z keyof Tkluczem, na przykład:

type ValueOf<T> = T[keyof T];

co ci daje

type Foo = { a: string, b: number };
type ValueOfFoo = ValueOf<Foo>; // string | number

W przypadku powyższego pytania możesz użyć pojedynczych klawiszy, węższych niż keyof T, aby wyodrębnić tylko typ wartości, na którym Ci zależy:

type sameAsString = Foo['a']; // lookup a in Foo
type sameAsNumber = Foo['b']; // lookup b in Foo

Aby mieć pewność, że para klucz / wartość „pasuje” do funkcji, należy używać typów ogólnych oraz typów wyszukiwania, na przykład:

declare function onChange<K extends keyof JWT>(key: K, value: JWT[K]): void; 
onChange('id', 'def456'); // okay
onChange('expire', new Date(2018, 3, 14)); // okay
onChange('expire', 1337); // error. 1337 not assignable to Date

Chodzi o to, że keyparametr pozwala kompilatorowi wywnioskować Kparametr ogólny . Następnie wymaga valuedopasowania JWT[K], typu wyszukiwania, którego potrzebujesz.

Mam nadzieję, że to pomaga; powodzenia!


Chcę stworzyć tuplez wartości. Czy jest na to szansa?
— Morteza Tourani

Brzmi to jak niepowiązane pytanie, więc prawdopodobnie powinieneś zadać je gdzie indziej, z minimalnym powtarzalnym przykładem , aby ludzie z pewnością zrozumieli Twój przypadek użycia. Powodzenia.
— jcalz

1
Wystąpił problem przy użyciu wyliczenia o wartości łańcuchowej z elementami członkowskimi funkcji. Aby dobrze sobie z tym poradzić, możesz użyć type StringValueOf<T> = T[keyof T] & string;. Najlepsze dokumenty, które znalazłem na
— wyliczeniach

41

Jeśli ktoś nadal szuka implementacji valueofdo jakichkolwiek celów, to jest taki, który wymyśliłem:

type valueof<T> = T[keyof T]

Stosowanie:

type actions = {
  a: {
    type: 'Reset'
    data: number
  }
  b: {
    type: 'Apply'
    data: string
  }
}
type actionValues = valueof<actions>

Działa zgodnie z oczekiwaniami :) Zwraca Unię wszystkich możliwych typów


2
To jest miłe. Prawie rozwiązuje mój inny problem , jednak przełącznik nie wydaje się w ogóle zawężać typu.
— styfle

Chcę stworzyć tuplez wartości. Czy jest na to szansa?
— Morteza Tourani

12

Istnieje inny sposób wyodrębnienia typu unii obiektu:

  const myObj = { a: 1, b: 'some_string' } as const;
  type values = typeof myObj[keyof typeof myObj];

Wynik: 1 | "some_string"


To constjest bardzo cenne, TypeScript w rzeczywistości dostarczy same wartości i usunie duplikaty; To bardzo dobre dla słowników.
— Noam

Dokładnie to, czego szukałem!
— Mavrick RMX '21

2

Dzięki istniejącym odpowiedziom, które doskonale rozwiązują problem. Chciałem tylko dodać, że biblioteka zawierała ten typ narzędzia, jeśli wolisz zaimportować ten popularny.

https://github.com/piotrwitek/utility-types#valuestypet

import { ValuesType } from 'utility-types';

type Props = { name: string; age: number; visible: boolean };
// Expect: string | number | boolean
type PropsValues = ValuesType<Props>;

2

Spróbuj tego:

type ValueOf<T> = T extends any[] ? T[number] : T[keyof T]

Działa na tablicy lub zwykłym obiekcie.

// type TEST1 = boolean | 42 | "heyhey"
type TEST1 = ValueOf<{ foo: 42, sort: 'heyhey', bool: boolean }>
// type TEST2 = 1 | 4 | 9 | "zzz..."
type TEST2 = ValueOf<[1, 4, 9, 'zzz...']>

0

Jedna linijka:

type ValueTypesOfPropFromMyCoolType = MyCoolType[keyof MyCoolType];

Przykład metody ogólnej:

declare function doStuff<V extends MyCoolType[keyof MyCoolType]>(propertyName: keyof MyCoolType, value: V) => void;
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.