web-dev-qa-db-fra.com

Clé d'inférence de chaîne | nombre lorsque la clé n'est qu'une chaîne

Je définis un AbstractModel comme ceci:

export interface AbstractModel {
   [key: string]: any
}

Ensuite, je déclare le type Keys:

export type Keys = keyof AbstractModel;

Je m'attendrais à ce que tout ce qui a le type Keys soit interprété de manière univoque comme une chaîne, par exemple:

const test: Keys;
test.toLowercase(); // Error: Property 'toLowerCase' does not exist on type 'string | number'. Property 'toLowerCase' does not exist on type 'number'.

Est-ce un bogue de TypeScript (2.9.2), ou ai-je raté quelque chose?

14
don

Comme défini dans les notes de publication de TypeScript 2.9, si vous saisissez une interface avec une signature d'index de chaîne, elle retourne une union de chaîne et de nombre

Étant donné un type d'objet X, la clé de X est résolue comme suit:

Si X contient une signature d'index de chaîne, keyof X est une union de chaîne, de nombre et de types littéraux représentant des propriétés de type symbole, sinon

Si X contient une signature d'index numérique, keyof X est une union de nombre et les types littéraux représentant des propriétés de type chaîne et de type symbole, sinon

keyof X est une union des types littéraux représentant des propriétés de type chaîne, de type nombre et de symbole.

source

En effet: JavaScript convertit les nombres en chaînes lors de l'indexation d'un objet:

[..] lors de l'indexation avec un nombre, JavaScript le convertit en fait en chaîne avant l'indexation en objet. Cela signifie que l'indexation avec 100 (un nombre) est la même chose que l'indexation avec "100" (une chaîne), donc les deux doivent être cohérents.

source

Exemple:

let abc: AbstractModel = {
    1: "one",
};

console.log(abc[1] === abc["1"]); // true

Lorsque vous ne voulez que les clés de chaîne, vous ne pouvez extraire les clés de chaîne de votre interface que comme suit:

type StringKeys = Extract<keyof AbstractModel, string>;

const test: StringKeys;
test.toLowerCase(); // no error

Le compilateur TypeScript fournit également une option pour obtenir le comportement antérieur à 2.9 de keyof:

keyofStringsOnly (booléen) par défaut false

Résolvez keyof en chaîne uniquement pour les noms de propriétés à valeur (pas de chiffres ni de symboles).

source

19
jmattheis

J'ai rencontré un problème similaire. Je l'ai résolu en appliquant la clé à une chaîne:

export type Keys = keyof AbstractModel & string;

Une autre option serait de convertir la clé en chaîne: test.toString().toLowercase()

3
madox2