Menu

Type guard en TypeScript : vérifier le type d'un objet

Un type guard est une vérification à l'exécution que TypeScript comprend. Découvrez les guards intégrés, comment écrire les vôtres avec un prédicat value is Type, comment vérifier qu'un objet est d'un type donné, les fonctions d'assertion avec asserts, et comment valider des données unknown.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

Un type guard est une vérification à l'exécution que TypeScript comprend : il affine donc le type dans la branche vérifiée. typeof, instanceof et in sont des guards intégrés ; pour tout le reste, vous écrivez une fonction dont le type de retour est un prédicat de type, value is Type.

isUser renvoie un simple booléen à l'exécution. Le type de retour value is User indique au compilateur ce que prouve un résultat true, et chaque if (isUser(x)) affine alors x en User.

Les type guards intégrés

Ces vérifications affinent sans aucune fonction auxiliaire :

GuardExempleÀ utiliser pour
typeoftypeof x === "number"primitives et fonctions
instanceofx instanceof Dateinstances de classes
in"email" in xunions d'objets, propriétés d'objets unknown
Array.isArrayArray.isArray(x)tableaux
Égalitéx === null, x.kind === "circle"null/undefined, étiquettes littérales
Véracitéif (x)retirer null et undefined

Tous s'exécutent comme du JavaScript ordinaire. Ce qu'ajoute TypeScript, c'est le narrowing : il lit la vérification et ajuste le type dans chaque branche. La liste complète des formes se trouve sur la page narrowing de types. Un guard personnalisé sert aux vérifications qui ne tiennent pas en une expression, ou que vous voulez réutiliser.

Écrire un prédicat de type

Un prédicat de type a la forme parameterName is Type et remplace boolean comme type de retour. Le narrowing fonctionne dans les deux sens : true affine vers Type, et false retire Type d'une union.

Passer un guard à filter donne un tableau correctement typé. Depuis TypeScript 5.5, le compilateur infère aussi un prédicat à partir des fonctions fléchées simples : pets.filter((p) => p.kind === "cat") renvoie donc Cat[] sans guard nommé.

Le type du prédicat doit correspondre au type du paramètre : function f(x: string): x is number provoque l'erreur TS2677, A type predicate's type must be assignable to its parameter's type.

Le compilateur fait confiance à votre guard

TypeScript vérifie qu'un guard renvoie un booléen. Il ne vérifie pas que ce booléen est juste. Un guard qui renvoie true pour de mauvaises valeurs fait mentir les types, et le programme échoue à l'exécution sans aucune erreur de compilation.

data.price.toFixed(2) lève TypeError: Cannot read properties of undefined (reading 'toFixed') à l'exécution. Le compilateur a accepté data.price comme un number parce que le guard l'affirmait. Vérifiez chaque propriété dont dépend le reste du code, et gardez les guards petits, testés et proches du type qu'ils décrivent.

Vérifier qu'un objet est d'un certain type

C'est la question derrière la plupart des guards personnalisés : des données arrivent en unknown (depuis JSON.parse, fetch, localStorage, un message) et vous devez savoir si elles correspondent à votre interface. La recette :

  1. typeof value === "object" && value !== null (un objet, pas null).
  2. "prop" in value pour chaque propriété requise. Sur unknown, in ajoute la propriété au type en tant que unknown.
  3. typeof value.prop === "..." (ou un guard imbriqué) pour le type de chaque propriété.
  4. Array.isArray(value.items) && value.items.every(isItem) pour les tableaux.

Pour les formes grandes ou profondément imbriquées, écrire ces guards à la main devient fastidieux. Les bibliothèques de schémas comme Zod ou Valibot vous permettent de décrire la forme une seule fois et vous donnent à la fois la vérification à l'exécution et le type TypeScript.

Fonctions d'assertion : asserts value is Type

Une fonction d'assertion lève une exception si la vérification échoue et se termine normalement sinon. Son type de retour est asserts value is Type (ou asserts condition), et tout ce qui vient après l'appel est affiné, sans if.

Une règle fait trébucher : une fonction d'assertion doit être appelée via un nom au type explicite. Une fonction fléchée const sans annotation, const check = (v: unknown): asserts v is string => {...}, provoque l'erreur TS2775 à l'appel, Assertions require every name in the call target to be declared with an explicit type annotation. Utilisez une déclaration function, ou annotez la constante avec un type de fonction.

Guards, assertions ou casts

OutilVérification à l'exécution ?AffineEn cas d'échec
Guard intégré (typeof, in...)Ouidans la brancheprend l'autre branche
Fonction value is TOui (votre code)dans la brancheprend l'autre branche
Fonction asserts value is TOui (votre code)après l'appellève une exception
value as TNonl'expressionrien : le mauvais type se propage

Une assertion de type (as) change le type sans rien vérifier. À une frontière où les données viennent de l'extérieur, un guard ou une fonction d'assertion est la version sûre de la même idée.

Guards basés sur this dans les classes

Une méthode peut affiner l'objet sur lequel elle est appelée avec this is Type. C'est pratique dans les hiérarchies de classes :

class FileNode {
  constructor(public name: string) {}
  isDirectory(): this is DirectoryNode {
    return this instanceof DirectoryNode;
  }
}

class DirectoryNode extends FileNode {
  children: FileNode[] = [];
}

function count(node: FileNode): number {
  return node.isDirectory() ? node.children.length : 0; // node: DirectoryNode in the true branch
}

Questions fréquentes

Qu'est-ce qu'un type guard en TypeScript ?

Toute vérification à l'exécution que TypeScript utilise pour affiner un type : typeof x === "string", x instanceof Date, "id" in x, Array.isArray(x), ou l'appel d'une fonction dont le type de retour est un prédicat de type comme x is User. Dans la branche vérifiée, la variable a le type plus étroit.

Comment vérifier qu'un objet est d'un certain type en TypeScript ?

Les types n'existent pas à l'exécution, vous vérifiez donc les propriétés : écrivez une fonction isUser(value: unknown): value is User qui teste typeof value === "object", value !== null, et chaque propriété requise avec in et typeof. Après if (isUser(x)), x est typé User. Pour les classes, x instanceof MyClass suffit.

Que signifie « value is Type » en TypeScript ?

C'est un prédicat de type, utilisé comme type de retour d'une fonction. La fonction renvoie toujours un booléen à l'exécution, mais quand elle renvoie true, TypeScript affine l'argument en Type à l'endroit de l'appel, et quand elle renvoie false, il l'affine vers les autres membres de l'union. Le compilateur ne vérifie pas le corps de la fonction : la vérification doit donc être correcte.

Quelle est la différence entre un type guard et une fonction d'assertion ?

Un type guard (x is T) renvoie un booléen et affine dans un if. Une fonction d'assertion (asserts x is T) ne renvoie rien et lève une exception quand la vérification échoue : tout ce qui suit l'appel est donc affiné sans if. Utilisez les guards pour les branchements et les assertions pour « ceci doit être vrai, sinon on s'arrête ».

Peut-on vérifier qu'un objet implémente une interface en TypeScript ?

Pas directement : les interfaces sont effacées et instanceof ne les accepte pas. Écrivez un type guard qui vérifie les propriétés de l'interface, ou ajoutez une propriété étiquette littérale (kind: "user") et comparez-la. Les bibliothèques de schémas comme Zod génèrent à la fois la vérification et le type à partir d'une seule définition.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER