192 votes

Pourquoi 'instanceof' en TypeScript me donne-t-il l'erreur "'Foo' ne fait référence qu'à un type, mais est utilisé ici comme une valeur."?

J'ai écrit ce code

interface Foo {
    abcdef: number;
}

let x: Foo | string;

if (x instanceof Foo) {
    // ...
}

Mais TypeScript m'a donné cette erreur :

'Foo' ne fait référence qu'à un type, mais est utilisé ici comme une valeur.

Pourquoi cela se produit-il ? Je pensais que instanceof pouvait vérifier si ma valeur avait un type donné, mais TypeScript semble ne pas aimer cela.

0 votes

Voir la réponse ci-dessous @4castle. Sinon, vous avez raison, je le ferai Foo | string.

1 votes

0 votes

Et doublon possible de Vérifier si la variable est d'un type d'interface spécifique dans une union typescript (Je ne veux pas vraiment marteler cela tout seul)

187voto

Daniel Rosenwasser Points 7171

TL;DR

instanceof fonctionne avec des classes, pas avec des interfaces ni des alias de types.


Que cherche à me dire TypeScript ?

Le problème est que instanceof est une construction de JavaScript, et en JavaScript, instanceof s'attend à une valeur du côté droit de l'opérande. Plus précisément, dans x instanceof Foo, JavaScript effectuera une vérification à l'exécution pour voir si Foo.prototype existe quelque part dans la chaîne de prototype de x.

Cependant, en TypeScript, les interface n'ont pas d'émission. Il en va de même pour les alias de type. Cela signifie que ni Foo ni Foo.prototype n'existent à l'exécution, donc ce code échouera certainement.

TypeScript essaie de vous dire que cela ne pourrait jamais fonctionner. Foo n'est qu'un type, ce n'est pas du tout une valeur !

Si vous venez d'une autre langue, vous avez peut-être voulu utiliser une classe ici. Les classes créent des valeurs à l'exécution, mais il y a certaines notes à ce sujet que vous voudrez peut-être lire ci-dessous.

"Que puis-je faire à la place de instanceof si je veux toujours un type ou une interface ?"

Vous pouvez regarder du côté des gardes de type et des gardes de type définis par l'utilisateur.

"Mais que se passe-t-il si je viens de passer d'une interface à une classe ?"

Vous pourriez être tenté de passer d'une interface à une classe, mais vous devez réaliser que dans le système de type structurel de TypeScript (où les choses sont principalement basées sur la forme), vous pouvez produire un objet qui a la même forme qu'une classe donnée :

class C {
    a: number = 10;
    b: boolean = true;
    c: string = "hello";
}

let x = new C()
let y: C = {
    a: 10, b: true, c: "hello",
}

// Fonctionne !
x = y;
y = x;

Dans ce cas, vous avez x et y qui ont le même type, mais si vous essayez d'utiliser instanceof sur l'un ou l'autre, vous obtiendrez le résultat opposé sur l'autre. Ainsi, instanceof ne vous dira vraiment pas grand-chose sur le type si vous profitez des types structurels en TypeScript.

2 votes

Il m'aurait fallu des âges pour repérer cela moi-même!

0 votes

Donc en gros je n'ai pas saisi l'idée de la réponse concernant ce qui est mieux à faire. Class ? parce que tu l'as détaillée. Mais en même temps, je suis confus comme tu l'as mentionné "vous pourriez être tenté". Alors que se passe-t-il si je dois comparer toutes les propriétés et pas seulement la propriété swim comme dans la documentation pour les gardes de type?

34 votes

Le point principal ici est que instanceof fonctionne avec des classes, pas des interfaces. Il fallait souligner cela.

20voto

CK Lee Points 2041

Pour effectuer une vérification de type à l'exécution avec une interface, utilisez les gardiens de type, si les interfaces que vous souhaitez vérifier ont des propriétés/fonctions différentes.

Exemple

let animal = getSmallPet();

if ((animal as Fish).swim) {
    (animal as Fish).swim();
} else if ((animal as Bird).fly) {
    (animal as Bird).fly();
}

0 votes

Que se passerait-il si j'apprenais à propos des canards et que j'ajoutais la fonction swim() à mon interface Bird? Est-ce que chaque animal de compagnie ne serait pas classé comme un poisson dans un garde de type? Et si j'avais trois interfaces avec trois fonctions chacune et que deux se chevauchent avec l'une des autres interfaces?

1 votes

@Kayz si vous n'avez pas de propriétés/fonctions qui identifient de manière unique une interface, vous ne pouvez pas vraiment les différencier. Votre animal de compagnie qui pourrait en fait être un Duck, vous le protégez par type, il devient Fish, mais toujours pas d'exception d'exécution lorsque vous invoquez swim(). Je vous suggère de créer 1 niveau d'interface commune (par exemple Swimmable) et de déplacer vos fonctions swim() là-bas, puis le type guard semble toujours bon avec ((pet as Swimmable).swim.

0 votes

Pour éviter la fusion des types, vous pouvez utiliser la condition 'swim' in pet. Cela permet de restreindre à un sous-ensemble qui doit avoir swim défini (exemple : Poisson | Mammifère)

3voto

Daniel Rosenwasser pourrait avoir raison et être fantastique, mais je sens le besoin d'apporter un amendement à sa réponse. Il est tout à fait possible de vérifier l'instance de x, regardez le code ci-dessous.

Mais il est tout aussi simple d'assigner x = y. Maintenant, x ne serait plus une instance de C car y avait seulement la forme de C.

class C {
a: number = 10;
b: boolean = true;
c: string = "hello";
}

let x = new C()
let y = {
    a: 10, b: true, c: "hello",
}

console.log('x est de type C ? ' + (x instanceof C)) // retourne true
console.log('y est de type C ? ' + (y instanceof C)) // retourne false

1voto

Janos Points 182

Quand il s'agit de vérifier si un objet est conforme à une signature d'interface, alors je pense que l'approche appropriée est de considérer l'utilisation de "prédicats de type": https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates

1voto

Giuseppedes Points 101

Vous pouvez utiliser le rétrécissement de l'opérateur in pour vérifier si l'élément dont vous avez besoin se trouve dans l'objet.

Avec cette méthode, vous pouvez vérifier si x est une chaîne de caractères ou un objet Foo

if ('abcdef' in x) {
    // x est une instance de Foo
}

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