web-dev-qa-db-fra.com

Existe-t-il une `valueof` similaire à` keyof` dans TypeScript?

Je veux pouvoir affecter une propriété d'objet à une valeur à partir d'une clé et d'une valeur en tant qu'entrées tout en étant capable de déterminer le type de la valeur. C'est un peu difficile à expliquer, donc ce code devrait révéler le problème:

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

J'ai essayé de changer value: any en value: valueof JWT mais cela n'a pas fonctionné.

Idéalement, onChange('expire', 1337) échouerait car 1337 n'est pas un type Date.

Comment puis-je changer value: any pour être la valeur de la clé donnée?

64
styfle

UPDATE: Le titre de la question semble attirer les personnes à la recherche d'une union de tous les types de valeur de propriété possibles, de la même manière que keyof vous donne l'union de tous les types de clé de propriété possibles. Aidons ces gens d'abord. Vous pouvez créer une ValueOf analogue à keyof, en utilisant types de recherche avec keyof T comme clé, comme suit:

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

qui te donne

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

Pour la question indiquée, vous pouvez utiliser des clés individuelles, plus étroites que keyof T, pour extraire uniquement le type de valeur qui vous tient à cœur:

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

Pour vous assurer que la paire clé/valeur "correspond bien" dans une fonction, vous devez utiliser génériques ainsi que des types de recherche, comme celui-ci:

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

L'idée est que le paramètre key permet au compilateur d'inférer le paramètre générique K. Ensuite, il faut que value corresponde à JWT[K], le type de recherche dont vous avez besoin.

J'espère que ça t'as aidé; bonne chance!

93
jcalz

Si quelqu'un cherche toujours l'implémentation de valueof à quelque fin que ce soit, voici une version que j'ai proposée:

type valueof<T> = T[keyof T]

Usage:

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

Fonctionne comme prévu :) Retourne une union de tous les types possibles

31
Chris Kowalski