TypeScript est un outil puissant pour construire des applications JavaScript évolutives et est presque devenu la norme pour les grands projets JavaScript Web. Cependant, pour les débutants, TypeScript a également des choses délicates, dont l'une se distingue entre les types d'union.
Considérez le code suivant:
interface chat { Poids: numéro; Whiskers: numéro; } chien d'interface { Poids: numéro; Friendly: booléen; } Laissez l'animal: chien | Cat;
De nombreux développeurs seront surpris de constater que lors de l'accès animal
, seul weight
est valide, tandis que whiskers
et les attributs friendly
ne sont pas valides. Cet article expliquera les raisons.
Avant de plonger, passons rapidement en revue les types structurels et nominaux, ce qui aidera à comprendre la distinction de TypeScript entre les types d'union.
La meilleure façon de comprendre les types structurels est de les comparer avec des types nominaux. La plupart des langues dactylographiées que vous pourriez avoir utilisées sont tapées nominalement. Par exemple, C # Code (Java ou C similaire):
classe foo { public int x; } classe Blah { public int x; }
Même si Foo
et Blah
ont exactement la même structure, ils ne peuvent pas s'attribuer des valeurs les uns aux autres. Le code suivant:
Blah b = new foo ();
L'erreur suivante sera générée:
<code>无法隐式转换类型“Foo”为“Blah”</code>
La structure de ces classes n'a pas d'importance. Une variable de type Foo
ne peut être attribuée qu'à une instance de Foo
(ou de sa sous-classe).
TypeScript, au contraire, adopte les types de structure. TypeScript les considère comme compatibles si les deux types ont la même structure.
Par conséquent, le code suivant fonctionne bien:
classe foo { x: nombre = 0; } classe Blah { x: nombre = 0; } Soit f: foo = new Blah (); Soit b: blah = new foo ();
Expliquons cela plus loin. Compte tenu du code suivant:
classe foo { x: nombre = 0; } Soit f: foo;
f
est une variable qui peut contenir n'importe quel objet qui est la même structure que Foo
, dans ce cas, ce qui signifie une propriété x
représentant un nombre. Cela signifie que même un objet JavaScript normal peut être accepté.
Soit f: foo; f = { x: 0 };
Revenons au code au début de cet article:
interface chat { Poids: numéro; Whiskers: numéro; } chien d'interface { Poids: numéro; Friendly: booléen; }
Nous savons:
Laissez l'animal: chien;
Faites animal
n'importe quel objet avec la même structure que l'interface Dog
. Alors, que signifie le code suivant?
Laissez l'animal: chien | Cat;
Cela définit le type d' animal
comme n'importe quel objet qui correspond à l'interface Dog
, ou tout objet qui correspond à l'interface Cat
.
Alors pourquoi animal
nous permet-il maintenant d'accéder weight
? Autrement dit, c'est parce que TypeScript ne sait pas de quel type il s'agit. TypeScript sait animal
doit être Dog
ou Cat
, mais cela peut être l'un ou l'autre. Si nous sommes autorisés à accéder à la propriété friendly
, mais qu'en cas animal
est en fait Cat
et non Dog
, cela peut entraîner une erreur d'exécution. Il en va de même pour whiskers
.
Le type d'union est une union de valeurs valides, et non une union d'attributs. Les développeurs écrivent souvent du code comme ceci:
Laissez l'animal: chien | Cat;
Et attendez-vous à ce que animal
aient une union de propriétés Dog
et Cat
. Mais c'est une autre erreur. Cela spécifie animal
a une valeur qui correspond à une articulation de valeurs Cat
Dog
valides et valides. Mais TypeScript vous permet uniquement d'accéder aux propriétés qu'il sait . Actuellement, cela signifie tous les types de propriétés de l'Union.
Maintenant, nous avons:
Laissez l'animal: chien | Cat;
Lorsque animal
est Dog
, comment le traitons-nous correctement comme des propriétés Dog
et d'accès sur l'interface Dog
, et aussi la gérer quand c'est Cat
? Actuellement, nous pouvons utiliser l'opérateur in
. Il s'agit d'un ancien opérateur JavaScript que vous ne voyez peut-être pas très souvent, ce qui nous permet de tester si une propriété existe dans un objet. Par exemple:
Soit O = {A: 12}; "A" en o; "x" dans o; // faux
Il s'avère que TypeScript est profondément intégré à l'opérateur in
. Voyons comment utiliser:
Laissez l'animal: chien | Cat = {} comme n'importe quel autre; if ("amical" en animal) { console.log (animal.friendly); } autre { console.log (animal.whiskers); }
Ce code ne produira pas d'erreurs. Dans le bloc if
, TypeScript sait qu'il y a une propriété friendly
, alors convertit animal
en Dog
. Dans le bloc else
, TypeScript traite de la même manière animal
comme Cat
. Vous pouvez même afficher cela dans l'éditeur de code en survolant votre souris sur un objet animal
dans ces blocs.
Vous pourriez vous attendre à ce que le billet de blog se termine ici, mais malheureusement, le rétrécissement du type syndical est très limité en vérifiant si l'attribut existe. Cela fonctionne bien pour nos types Dog
et Cat
simples, mais lorsque nous avons plus de types et plus de chevauchement entre ces types, les choses peuvent facilement devenir plus complexes et fragiles.
C'est là que la différenciation du type syndical est utile. Nous allons tout garder d'avant, il suffit d'ajouter une propriété à chaque type, dont le seul but est de distinguer (ou de "différencier" ces types:
interface chat { Poids: numéro; Whiskers: numéro; Animal_type: "Cat"; } chien d'interface { Poids: numéro; Friendly: booléen; Animal_type: "chien"; }
Notez l'attribut ANIMAL_TYPE
sur les deux types. Ne le confondez pas avec une chaîne avec deux valeurs différentes; ANIMAL_TYPE: "CAT";
Maintenant, notre inspection est devenue plus fiable:
Laissez l'animal: chien | Cat = {} comme n'importe quel autre; if (animal.animal_type === "chien") { console.log (animal.friendly); } autre { console.log (animal.whiskers); }
Ce chèque sera infaillible en supposant que chaque type participant à l'Union a une valeur différente de l'attribut ANIMAL_TYPE
.
Le seul inconvénient est que vous devez maintenant faire face à une nouvelle propriété. Chaque fois qu'une instance de Dog
ou Cat
est créée, vous devez fournir la valeur correcte unique pour ANIMAL_TYPE
. Mais ne vous inquiétez pas d'oublier, car TypeScript vous rappellera. ?
Si vous souhaitez en savoir plus, je recommande de lire la documentation TypeScript sur le rétrécissement du type. Cela nous donnera un aperçu plus approfondi de ce dont nous discutons ici. Il y a une section sur les assertions de type dans ce lien. Ceux-ci vous permettent de définir vos propres vérifications personnalisées pour affiner les types sans utiliser de discriminateurs de type ou s'appuyer sur le in
-clé.
Au début de cet article, j'ai dit, dans l'exemple suivant, pourquoi weight
est la seule propriété accessible:
interface chat { Poids: numéro; Whiskers: numéro; } chien d'interface { Poids: numéro; Friendly: booléen; } Laissez l'animal: chien | Cat;
Ce que nous avons appris, c'est que TypeScript ne sait animal
peut être Dog
ou Cat
, mais pas les deux. Par conséquent, nous ne pouvons que prendre weight
, qui est la seule propriété publique entre les deux.
Le concept de distinction des types d'union est la façon dont TypeScript distingue ces objets, et cette approche est très évolutive, même pour les plus grandes collections d'objets. Par conséquent, nous devons créer une nouvelle propriété ANIMAL_TYPE
sur les deux types qui contient une seule valeur littérale que nous pouvons utiliser pour vérifier. Bien sûr, c'est une autre chose qui doit être suivie, mais elle produit également des résultats plus fiables - ce qui est exactement ce que nous voulons de TypeScript en premier lieu.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!