58
votes

Que signifie «keyof typeof» dans TypeScript?

Exemple:

Expliquez-moi ce keyof typeof signifie keyof typeof dans TypeScript

type Colors = "white" | "black"

La dernière ligne équivaut à:

enum ColorsEnum {
    white = '#ffffff',
    black = '#000000',
}

type Colors = keyof typeof ColorsEnum;

Mais comment ça fonctionne?

Je m'attendrais à ce que typeof ColorsEnum renvoie quelque chose comme "Object" et ensuite keyof "Object" pour ne rien faire d'intéressant. Mais je me trompe évidemment.


0 commentaires

4 Réponses :


27
votes

Une enum crée un object instancié. Avec typeof nous obtenons le type généré automatiquement de cette enum .

Nous pouvons maintenant obtenir tous les index avec keyof pour nous assurer que les Colors ne peuvent en contenir qu'un seul.


0 commentaires

86
votes

Pour comprendre l'utilisation de la keyof typeof dans Typescript, vous devez d'abord comprendre ce que sont les types littéraux et l' union des types littéraux . Donc, je vais d'abord expliquer ces concepts, puis expliquer en détail keyof et typeof individuellement. Après cela, je reviendrai à enum pour répondre à ce qui est demandé dans la question. C'est une réponse longue mais les exemples sont faciles à comprendre.


Types littéraux

Les types littéraux dans Typescript sont des types plus spécifiques de string , de number ou de boolean . Par exemple, "Hello World" est une string , mais une string n'est pas "Hello World" . "Hello World" est un type de string de type plus spécifique, il s'agit donc d'un type littéral.

Un type littéral peut être déclaré comme suit:

type Colors = keyof typeof ColorsEnum

let colorLiteral: Colors
colorLiteral = "white"  // OK
colorLiteral = "black"  // OK
colorLiteral = "red"    // Error...

Cela signifie que l'objet de type Greeting ne peut avoir qu'une valeur de string "Hello" et aucune autre valeur de chaîne ou toute autre valeur de tout autre type, comme indiqué dans le code suivant:

enum ColorsEnum {
    white = '#ffffff',
    black = '#000000',
}

Les types littéraux ne sont pas utiles en eux-mêmes, mais lorsqu'ils sont combinés avec des types d'union, des alias de type et des protections de type, ils deviennent puissants.

Voici un exemple d' union de types littéraux :

type CarLiteralType = keyof typeof bmw

let carPropertyLiteral: CarLiteralType
carPropertyLiteral = "name"       // OK
carPropertyLiteral = "power"      // OK
carPropertyLiteral = "anyOther"   // Error...

Désormais, l'objet de type Greeting peut avoir la valeur "Hello" , "Hi" ou "Welcome" .

const bmw = { name: "BMW", power: "1000hp" }

keyof seulement

keyof d'un type T vous donne un nouveau type qui est une union de types littéraux et ces types littéraux sont les noms des propriétés de T Le type résultant est un sous-type de chaîne.

Par exemple, considérez l' interface suivante:

let newTypeObject: SomeNewType
newTypeObject = "name"           // OK
newTypeObject = "age"            // OK
newTypeObject = "location"       // OK
newTypeObject = "anyOtherValue"  // Error...

L'utilisation de l'opérateur keyof sur le type Person vous donnera un nouveau type comme indiqué dans le code suivant:

type SomeNewType = keyof Person

Ce SomeNewType est une union de types littéraux ( "name" | "age" | "location" ) qui est faite à partir des propriétés de type Person .

Vous pouvez maintenant créer des objets de type SomeNewType :

interface Person {
    name: string
    age: number
    location: string
}

keyof typeof ensemble sur un objet

Comme vous le savez peut-être déjà, l'opérateur typeof vous donne le type d'un objet. Dans l'exemple ci-dessus de l'interface Person , nous connaissions déjà le type donc, nous devions simplement utiliser l'opérateur keyof sur le type Person .

Mais que faire lorsque nous ne connaissons pas le type d'un objet ou que nous avons juste une valeur et non un type de cette valeur comme suit?

let greeting: Greeting
greeting = "Hello"       // OK
greeting = "Hi"          // OK
greeting = "Welcome"     // OK
greeting = "GoodEvening" // Error: Type '"GoodEvening"' is not assignable to type 'Greeting'

C'est là que nous utilisons ensemble keyof typeof .

Le typeof bmw vous donne le type: { name: string, power: string }

Et puis l'opérateur keyof vous donne l'union de type littéral comme indiqué dans le code suivant:

type Greeting = "Hello" | "Hi" | "Welcome"

keyof typeof sur une enum

Dans Typescript, les Enums sont des objets réels. Ainsi, l'explication des objets ci-dessus est applicable ici aussi. L'exemple donné par OP dans la question est:

let greeting: Greeting
greeting = "Hello" // OK
greeting = "Hi"    // Error: Type '"Hi"' is not assignable to type '"Hello"'

Ici, ColorsEnum est un objet, pas un type. Nous devons donc appeler les opérateurs keyof typeof ensemble, comme indiqué dans le code suivant:

type Greeting = "Hello"

C'est ça! J'espère que ça t'as aidé.


4 commentaires

Pourquoi diable cela n'a-t-il que deux, trois voix?


Appréciez la réponse! Il devrait y avoir beaucoup plus de votes positifs!


Il s'agit d'une réponse archétypale de Stack Overflow et doit être utilisée pour expliquer aux nouveaux utilisateurs comment répondre à une question. Incroyablement bon, et cela devrait être la réponse acceptée. Excellent.


@Yogesh Umesh Vaity. Taper sur! Très bien expliqué!



9
votes

Idée fausse commune sur TypeScript

TypeScript est souvent décrit comme une couche de type au-dessus du runtime JavaScript. Comme si les types et les valeurs vivaient sur des plans séparés. Cependant, dans TypeScript, certaines choses sont des types et des valeurs à la fois.

Ceci est vrai pour:

  • Des classes,
  • énumérations,
  • espaces de noms.

Quand pouvez-vous utiliser keyof ?

Le mot clé keyof ne fonctionne qu'au niveau du type. Vous ne pouvez pas l'appliquer à une valeur JavaScript.

Quand avez-vous besoin de keyof typeof ?

Lorsque vous avez affaire à quelque chose qui est à la fois un type et une valeur (comme une classe ou une énumération), mais que vous vous intéressez spécifiquement au type de cette valeur.

L'exemple le plus simple:

declare class Foo {
    static staticProperty: string;

    dynamicProperty: string;
}

type Constructor = typeof Foo;
type Instance = Foo;

type A = keyof Constructor; // "prototype" | "staticProperty"
type B = keyof Instance; // "dynamicProperty"

En général, quand vous voyez ceci:

type A = keyof typeof B;

la partie typeof B indique à TypeScript de regarder le type de B. Vous pouvez le considérer comme un transtypage de B en son type. Un peu comme lancer un objet bidimensionnel dans un espace unidimensionnel.

Puisque typeof B est un type, pas une valeur, nous pouvons maintenant utiliser keyof dessus.

Exemple

Les classes sont des types et des valeurs. Vous pouvez les appeler, mais vous pouvez également utiliser keyof sur eux.

const foo = { bar: 42 }; // foo is a value
type Foo = typeof foo; // Foo is the type of foo

type KeyOfFoo = keyof Foo; // "keyof Foo" is the same as "keyof typeof foo", which is "bar"

En utilisant typeof avec keyof , nous pouvons basculer entre l'utilisation de keyof contre le type d' instance et le type de constructeur .


0 commentaires

0
votes

Pour trouver le type de toutes les valeurs, nous utilisons l'opération typeof. Pour par exemple

type userKeys = keyof userType

Ici, l'utilisateur est une valeur, donc ici l'opérateur typeof est utile

type userType = typeof user

Ici, userType donne des informations de type selon lesquelles l'utilisateur est un objet qui a deux propriétés getPersonalInfo et getLocation et les deux sont des fonctions return void

Maintenant, si vous voulez trouver les clés de l'utilisateur, vous pouvez utiliser keyof

const user = {
   getPersonalInfo(){},
   getLocation(){}
}

qui dit userKeys = 'getPersonalInfo' | 'getLocation'

Méfiez-vous si vous essayez d'obtenir la clé de l'utilisateur comme le type userKeys = keyof user vous obtiendrez une erreur 'user' se réfère à une valeur, mais est utilisé comme type ici. Vouliez-vous dire 'typeof user'?


0 commentaires