web-dev-qa-db-fra.com

Quel est le type d'enregistrement en dactylographié?

Que signifie Record<K, T> dans Typescript?

TypeScript 2.1 a introduit le type Record, en le décrivant dans un exemple:

// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>

voir TypeScript 2.1

Et la page Advanced Types mentionne Record sous l'en-tête Types mappés à côté de Readonly, Partial et Pick, dans ce qui semble être sa définition. :

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

Readonly, Partial et Pick sont homomorphes alors que Record ne l’est pas. Un indice que Record n’est pas homomorphique est qu’il n’est pas nécessaire de saisir un type d’entrée pour copier les propriétés:

type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

Et c'est tout. Outre les citations ci-dessus, il n'y a pas d'autre mention de Record sur typescriptlang.org .

Des questions

  1. Quelqu'un peut-il donner une définition simple de ce que Record est?

  2. Est-ce que Record<K,T> est simplement un moyen de dire "toutes les propriétés de cet objet auront le type T"? Probablement pas toutes les propriétés , puisque K a une certaine utilité ...

  3. Le générique K interdit-il des clés supplémentaires sur l'objet qui ne sont pas K, ou les autorise-t-il et indique-t-il simplement que leurs propriétés ne sont pas transformées en T?

  4. Avec l'exemple donné:

    type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
    

    Est-ce exactement la même chose ?:

    type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
    
89
Matthias Dailey
  1. Quelqu'un peut-il donner une définition simple de ce que Record est?

Un Record<K, T> est un type d'objet dont les clés de propriété sont K et dont les valeurs de propriété sont T. Autrement dit, keyof Record<K, T> est équivalent à K, et Record<K, T>[K] est (fondamentalement) équivalent à T.

  1. Est-ce que Record<K,T> est simplement un moyen de dire "toutes les propriétés de cet objet auront le type T"? Probablement pas tous les objets, puisque K a une raison d'être ...

Comme vous le constatez, K a pour but ... de limiter les clés de propriété à des valeurs particulières. Si vous voulez accepter toutes les clés possibles avec une valeur de chaîne, vous pouvez faire quelque chose comme Record<string, T>, mais la façon idiomatique de le faire est d'utiliser un signature d'index comme { [k: string]: T }.

  1. Le générique K interdit-il des clés supplémentaires sur l'objet qui ne sont pas K, ou les autorise-t-il et indique-t-il simplement que leurs propriétés ne sont pas transformées en T?

Cela n'interdit pas exactement les clés supplémentaires: après tout, une valeur est généralement autorisée à avoir des propriétés non mentionnées explicitement dans son type ... mais elle ne reconnaît pas que de telles propriétés existent:

declare const x: Record<"a", string>;
x.b; // error, Property 'b' does not exist on type 'Record<"a", string>'

et il les traiterait comme propriétés excessives qui sont parfois rejetées:

declare function acceptR(x: Record<"a", string>): void;
acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties

et parfois accepté:

const y = {a: "hey", b: "you"};
acceptR(y); // okay
  1. Avec l'exemple donné:

    type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
    

    Est-ce exactement la même chose ?:

    type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
    

Oui!

J'espère que ça t'as aidé. Bonne chance!

105
jcalz

Un enregistrement vous permet de créer un nouveau type à partir d'une union. Les valeurs dans l'Union sont utilisées en tant qu'attributs du nouveau type.

Par exemple, disons que j'ai une Union comme celle-ci:

type CatNames = "miffy" | "boris" | "mordred";

Maintenant, je veux créer un objet contenant des informations sur tous les chats, je peux créer un nouveau type en utilisant les valeurs de l'union CatName comme clés.

type CatList = Record<CatNames, {age: number}>

Si je veux satisfaire cette CatList, je dois créer un objet comme celui-ci:

const cats:CatList = {
  miffy: { age:99 },
  boris: { age:16 },
  mordred: { age:600 }
}

Vous obtenez une sécurité de type très forte:

  • Si j'oublie un chat, j'obtiens une erreur.
  • Si j'ajoute un chat qui n'est pas autorisé, j'obtiens une erreur.
  • Si je change plus tard de CatNames, j'obtiens une erreur. Ceci est particulièrement utile car les noms de fichiers sont probablement importés d'un autre fichier et utilisés à de nombreux endroits.

Monde réel React exemple.

Je l'ai utilisé récemment pour créer un composant Status. Le composant recevrait un accessoire d'état, puis rendrait une icône. J'ai beaucoup simplifié le code ici à des fins d'illustration

J'ai eu un syndicat comme celui-ci:

type Statuses = "failed" | "complete";

J'ai utilisé cela pour créer un objet comme celui-ci:

const icons: Record<
  Statuses,
  { iconType: IconTypes; iconColor: IconColors }
> = {
  failed: {
    iconType: "warning",
    iconColor: "red"
  },
  complete: {
    iconType: "check",
    iconColor: "green"
  };

Je pourrais ensuite rendre le rendu en déstructurant un élément de l'objet en accessoires, comme ceci:

const Status = ({status}) => <Icon {...icons[status]} />

Si l'union des statuts est ultérieurement étendue ou modifiée, je sais que la compilation de mon composant Status échouera et j'obtiendrai une erreur que je pourrai immédiatement corriger. Cela me permet d'ajouter des états d'erreur supplémentaires à l'application.

Notez que l'application réelle comportait des dizaines d'états d'erreur référencés à plusieurs endroits. Ce type de sécurité était donc extrêmement utile.

19
superluminary