web-dev-qa-db-fra.com

Typescript: Vérifie "typeof" par rapport au type personnalisé

J'ai un type personnalisé, disons

export type Fruit = "Apple" | "banana" | "grape";

Je voudrais déterminer si une chaîne fait partie du type Fruit. Comment puis-je accomplir cela?

Ce qui suit ne fonctionne pas.

let myfruit = "pear";
if (typeof myfruit === "Fruit") {
    console.log("My fruit is of type 'Fruit'");
}

Toutes les pensées appréciées!

14
maia

Vous pourriez être dérouté par la différence entre les valeurs et les types dans TypeScript, en particulier en ce qui concerne l'opérateur typeof. Comme vous le savez peut-être, TypeScript ajoute un système de types statique à JavaScript et ce système de types est effacé lorsque le code est transpilé . La syntaxe de TypeScript est telle que certaines expressions et instructions font référence à des valeurs existant au moment de l'exécution, tandis que d'autres expressions et instructions font référence à types qui n'existent qu'au moment de la conception/compilation. Les valeurs ont des types , mais ce ne sont pas des types eux-mêmes. Il est important de noter qu'il existe des endroits dans le code où le compilateur attend une valeur et interprète l'expression qu'il trouve comme valeur si possible, et d'autres endroits où le compilateur attend un type et interprète l'expression qu'il trouve comme type si possible.

L'opérateur typeof mène une double vie. L'expression typeof x S'attend toujours à ce que x soit une valeur, mais typeof x Lui-même pourrait être une valeur ou un type en fonction du contexte:

let bar = {a: 0};
let TypeofBar = typeof bar; // the value "object"
type TypeofBar = typeof bar; // the type {a: number}

La ligne let TypeofBar = typeof bar; Passera au JavaScript et utilisera l'opérateur JavaScript typeof au moment de l'exécution pour produire une chaîne. Mais type TypeofBar = typeof bar; est effacé et utilise = l'opérateur de requête de type TypeScript pour examiner le type statique que TypeScript a attribué à la valeur nommée bar.

Dans votre code,

let myfruit = "pear";
if (typeof myfruit === "Fruit") { // "string" === "Fruit" ?!
    console.log("My fruit is of type 'Fruit'");
}

typeof myfruit Est une valeur, pas un type. Il s’agit donc de l’opérateur JavaScript typeof et non de l’opérateur de requête de type TypeScript. Il retournera toujours la valeur "string"; ce ne sera jamais Fruit ou "Fruit". Vous ne pouvez pas obtenir les résultats de l'opérateur de requête de type TypeScript au moment de l'exécution, car le système de types est effacé au moment de l'exécution. Vous devez abandonner l'opérateur typeof.


Ce que vous pouvez faire, c'est vérifier la valeur de myfruit par rapport aux trois littéraux de chaîne Fruit connus ... comme, par exemple, ceci:

let myfruit = "pear";
if (myfruit === "Apple" || myfruit === "banana" || myfruit === "grape") {
  console.log("My fruit is of type 'Fruit'");
}

Parfait, non? Ok, peut-être que cela ressemble à beaucoup de code redondant. Voici une façon moins redondante de le faire. Tout d’abord, définissez votre type Fruit en fonction d’un tableau de valeurs littérales existant ... TypeScript peut déduire des types à partir de valeurs, mais vous ne pouvez pas générer de valeurs à partir de types.

const stringLitArray = <L extends string>(arr: L[]) => arr;
const fruit = stringLitArray(["Apple", "banana", "grape"]);
export type Fruit = (typeof fruit)[number];

Vous pouvez vérifier que Fruit est du même type que vous vous êtes défini manuellement. Ensuite, pour le test de type, vous pouvez utiliser un type défini par l'utilisateur comme ceci:

const isFruit = (x: any): x is Fruit => fruit.includes(x);

isFruit() est une fonction qui vérifie si son argument est trouvé dans le tableau fruit et, le cas échéant, réduit le type de son argument à Fruit. Voyons voir que ça marche:

let myfruit = "pear";
if (isFruit(myfruit)) {
  console.log("My fruit is of type 'Fruit'");
}

Ce type guard permet également au compilateur de savoir que, dans la clause "then" de l'instruction if, myfruit est un Fruit. Imaginez si vous aviez une fonction qui n'accepte que Fruit, et une valeur qui pourrait ou non être un Fruit:

declare function acceptFruit(f: Fruit): void;
const myfruit = Math.random() < 0.5 ? "pear" : "banana";

Vous ne pouvez pas appeler la fonction directement:

acceptFruit(myfruit); // error, myfruit might be "pear"

Mais vous pouvez l'appeler à l'intérieur de la clause "then" après l'avoir vérifiée:

if (isFruit(myfruit)) {
  acceptFruit(myfruit); // okay, myfruit is known to be "banana"
}

Ce qui est probablement la raison pour laquelle vous souhaitez vérifier votre type personnalisé en premier lieu. Cela vous permet donc de le faire.


Pour récapituler: vous ne pouvez pas utiliser typeof. Vous pouvez comparer avec des chaînes. Vous pouvez faire une inférence de type et une garde de type pour éliminer le code en double et obtenir une analyse de type de flux de contrôle du compilateur.

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

41
jcalz