6 votes

Pourquoi le nouveau type `Pick<T, K extends keyof T>` autorise-t-il les sous-ensembles de `K` dans la fonction `setState()` de React ?

Je pensais avoir compris le but de la nouvelle TS 2.1 Pick type mais ensuite j'ai vu comment il était utilisé dans les définitions de type React et je ne comprends pas :

declare class Component<S> {
    setState<K extends keyof S>(state: Pick<S, K>, callback?: () => any): void;
    state: Readonly<S>;
}

Ce qui vous permet de faire ça :

interface PersonProps {
  name: string;
  age: number;
}

class Person extends Component<{}, PersonProps> {
  test() {
    this.setState({ age: 123 });
  }
}

Ma confusion ici est que keyof S es { name, age } mais j'appelle setState() avec seulement age -- pourquoi ne se plaint-il pas de la disparition name ?

Ma première pensée est que parce que Pick est un type d'index, il n'est simplement pas nécessaire que toutes les clés existent. C'est logique. Mais si j'essaie d'assigner le type directement :

const ageState: Pick<PersonProps, keyof PersonProps> = { age: 123 };

Il fait se plaignent de l'absence name clé :

Type '{ age: number; }' is not assignable to type 'Pick<PersonProps, "name" | "age">'.
  Property 'name' is missing in type '{ age: number; }'.

Je ne comprends pas ça. C'est semble Je n'ai fait que remplir S avec le type que S est déjà attribué, et il est passé de l'autorisation d'un sous-ensemble de clés pour exiger tous les clés. C'est une grande différence. Le voici dans la cour de récréation%3A%20void%3B%0A%20%20%20%20state%3A%20Readonly%3CS%3E%3B%0A%7D%0A%0Ainterface%20PersonProps%20%7B%0A%20%20name%3A%20string%3B%0A%20%20age%3A%20number%3B%0A%7D%0A%0Aclass%20Person%20extends%20Component%3C%7B%7D%2C%20PersonProps%3E%20%7B%0A%20%20test()%20%7B%0A%20%20%20%20const%20person%3A%20Pick%3CPersonProps%2C%20keyof%20PersonProps%3E%20%3D%20%7B%20age%3A%20123%20%7D%3B%0A%20%20%20%20this.setState(%7B%20age%3A%20123%20%7D)%3B%0A%20%20%7D%0A%7D) . Quelqu'un peut-il expliquer ce comportement ?

8voto

Steven Barnett Points 1018

Réponse courte : si vous voulez vraiment un type explicite, vous pouvez utiliser Pick<PersonProps, "age"> mais il est plus facile d'utiliser des types implicites à la place.

Longue réponse :

Le point essentiel est que le K est une variable de type générique qui étend keyof T .

Le type keyof PersonProps est égal à l'union de chaînes "name" | "age" . Le type "age" peut être considéré comme étendant le type "name" | "age" .

Rappelons la définition de Pick est :

type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
}

ce qui signifie que pour chaque K l'objet décrit par ce type doit avoir une propriété P du même type que la propriété K en T . Votre exemple de code de terrain de jeu était :

const person: Pick<PersonProps, keyof PersonProps> = { age: 123 };

En dénouant les variables de type générique, on obtient :

  • Pick<T, K extends keyof T> ,
  • Pick<PersonProps, "name" | "age"> ,
  • [P in "name" | "age"]: PersonProps[P] et enfin
  • {name: string, age: number} .

Ceci est, bien sûr, incompatible avec { age: 123 } . Si vous dites à la place :

const person: Pick<PersonProps, "age"> = { age: 123 };

alors, suivant la même logique, le type de person sera correctement équivalent à {age: number} .

Bien sûr, TypeScript calcule tous ces types pour vous de toute façon - c'est ainsi que vous avez obtenu l'erreur. Puisque TypeScript connaît déjà les types {age: number} y Pick<PersonProps, "age"> sont compatibles, il vaut mieux garder le type implicite :

const person = { age: 123 };

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X