web-dev-qa-db-fra.com

Comment vérifier le type d'objet lors de l'exécution dans TypeScript?

J'essaie de trouver un moyen de passer un objet pour qu'il fonctionne et de le vérifier dans une exécution. C'est un pseudo code:

func(obj:any){
  if(typeof obj === "A"){
    // do something
  }
  else if(typeof obj === "B"{
    //do something else
  }

}
 a:A;
 b:B;
 func(a);

Mais typeof renvoie toujours "objet" et je n’ai pas trouvé le moyen d’obtenir le type réel de "a" ou de "b". L'instance de n'a pas non plus fonctionné et retourne le même . Avez-vous une idée de la façon de le faire dans un TypeScript?

Merci de votre aide!!!

9
Eden1971

Les types sont supprimés au moment de la compilation et n'existent pas au moment de l'exécution, vous ne pouvez donc pas vérifier le type au moment de l'exécution.

Ce que vous pouvez faire est de vérifier que la forme d'un objet correspond à vos attentes, et TypeScript peut affirmer le type lors de la compilation à l'aide d'un type défini par l'utilisateur qui renvoie la valeur true si la forme correspond à vos attentes:

interface A {
  foo: string;
}

interface B {
  bar: number;
}

function isA(obj: any): obj is A {
  return obj.foo !== undefined 
}

function isB(obj: any): obj is B {
  return obj.bar !== undefined 
}

function func(obj: any) {
  if (isA(obj)) {
    // In this block 'obj' is narrowed to type 'A'
    obj.foo;
  }
  else if (isB(obj)) {
    // In this block 'obj' is narrowed to type 'B'
    obj.bar;
  }
}

Exemple sur le terrain de jeu

22
Aaron

Pour donner suite à la réponse d’Aaron, j’ai fabriqué un transformateur qui génère les fonctions de type guard au moment de la compilation. De cette façon, vous n'avez pas à les écrire manuellement.

Par exemple:

import { is } from 'TypeScript-is';

interface A {
  foo: string;
}

interface B {
  bar: number;
}

if (is<A>(obj)) {
  // obj is narrowed to type A
}

if (is<B>(obj)) {
  // obj is narrowed to type B
}

Vous pouvez trouver le projet ici, avec les instructions pour l'utiliser:

https://github.com/woutervh-/TypeScript-is

3
user7132587

La question des OP était "J'essaie de trouver un moyen de transmettre un objet pour qu'il fonctionne et de le vérifier dans un runtime". 

Comme une instance de classe est simplement un objet, la bonne réponse consiste à utiliser une instance de classe et une instance de vérification du type à l'exécution, utilisez interface si ce n'est pas le cas. 

Dans ma base de code, j'ai généralement une classe qui implémente une interface et l'interface est utilisée lors de la compilation pour la sécurité du type avant la compilation, tandis que les classes sont utilisées pour organiser mon code ainsi que pour effectuer des vérifications de type à l'exécution à TypeScript.

Fonctionne car routerEvent est une instance de la classe NavigationStart

if (routerEvent instanceof NavigationStart) {
  this.loading = true;
}

if (routerEvent instanceof NavigationEnd ||
  routerEvent instanceof NavigationCancel ||
  routerEvent instanceof NavigationError) {
  this.loading = false;
}

Ne fonctionnera pas

// Must use a class not an interface
export interface IRouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true); 

Ne fonctionnera pas

// Must use a class not a type
export type RouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true); 

Comme vous pouvez le constater avec le code ci-dessus, les classes sont utilisées pour comparer l'instance aux types NavigationStart | Cancel | Error. 

Utiliser instanceof sur un type ou une interface n'est pas possible, car le compilateur ts élimine ces attributs pendant son processus de compilation et avant d'être interprété par JIT ou AOT. Les classes sont un excellent moyen de créer un type utilisable aussi bien lors de la précompilation que lors de l'exécution de JS.

0
Alpha G33k

J'ai joué avec la réponse d'Aaron et je pense qu'il serait préférable de tester typeof plutôt que simplement indéfini, comme ceci:

interface A {
  foo: string;
}

interface B {
  bar: number;
}

function isA(obj: any): obj is A {
  return typeof obj.foo === 'string' 
}

function isB(obj: any): obj is B {
  return typeof obj.bar === 'number' 
}

function func(obj: any) {
  if (isA(obj)) {
    console.log("A.foo:", obj.foo);
  }
  else if (isB(obj)) {
    console.log("B.bar:", obj.bar);
  }
  else {console.log("neither A nor B")}
}

const a: A = { foo: 567 }; // notice i am giving it a number, not a string 
const b: B = { bar: 123 };

func(a);  // neither A nor B
func(b);  // B.bar: 123
0
Carl Sorenson