Menu

typeof en TypeScript : type guards et opérateur de type typeof

typeof a deux rôles en TypeScript. Dans le code, c'est l'opérateur JavaScript qui renvoie "string", "number", "object" et ainsi de suite à l'exécution, et TypeScript affine les types à partir de lui. Dans un type, c'est la requête de type qui copie le type d'une variable, comme dans keyof typeof obj et ReturnType<typeof fn>.

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

typeof a deux sens en TypeScript. Dans une expression, c'est l'opérateur JavaScript : il s'exécute, renvoie une chaîne comme "string" ou "number", et TypeScript affine la variable quand vous comparez cette chaîne. En position de type, c'est une requête de type : typeof config donne le type statique de la variable config.

La dernière ligne montre la différence : typeof custom dans un console.log est l'opérateur d'exécution et affiche object, tandis que typeof defaults après type Options = est la requête de compilation et n'atteint jamais le JavaScript.

Les deux sens en un coup d'œil

typeof à l'exécution (JavaScript)Requête de type typeof (TypeScript)
Où il apparaîtDans n'importe quelle expression : if, return, console.log(...)En position de type : après :, dans type X = ..., entre <...>
Exempletypeof x === "string"let y: typeof x;
ÉvaluéQuand le code s'exécutePar le compilateur, puis effacé
RésultatUne chaîne parmi huitUn type
Sert àVérifier une valeur et affiner son typeDériver un type à partir d'une valeur

TypeScript les distingue par leur position : le même mot-clé n'a jamais les deux sens à la fois.

Résultats de typeof à l'exécution

L'opérateur d'exécution renvoie l'une de huit chaînes. Exécutez ce code pour voir ce que donnent les valeurs courantes :

ValeurRésultat de typeofRemarque
"hi", template strings"string"
42, 3.14, NaN, Infinity"number"NaN est un nombre ; vérifiez-le avec Number.isNaN
10n"bigint"
true, false"boolean"
undefined"undefined"
Symbol("id")"symbol"
fonctions, fonctions fléchées, classes"function"une classe est une fonction constructeur
null"object"un bug historique de JavaScript
{}, [], new Date(), new Map()"object"les tableaux sont des objets : utilisez Array.isArray
new String("x"), new Number(1)"object"objets enveloppes, à éviter

En JavaScript pur, typeof sur une variable jamais déclarée renvoie "undefined" au lieu de lever une exception. En TypeScript, ce code ne compile pas : Cannot find name 'notDeclared'. (TS2304).

Affiner avec typeof

Une comparaison typeof dans un if, un switch, un ternaire ou après && affine la variable dans chaque branche. C'est la façon standard de traiter une union de primitives, et la première vérification à utiliser sur unknown.

typeof x !== "string" affine dans l'autre sens, vers tout sauf string. Et le compilateur connaît les huit chaînes possibles : une faute de frappe dans la comparaison est donc une erreur de compilation plutôt qu'une vérification toujours fausse en silence :

Le compilateur affiche index.ts(3,7): error TS2367: This comparison appears to be unintentional because the types '"bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined"' and '"strnig"' have no overlap. En JavaScript pur, la faute de frappe enverrait discrètement chaque chaîne vers le chemin number. Corrigez l'orthographe et le code affiche text.

Le piège de typeof null

Comme typeof null vaut "object", TypeScript affine typeof x === "object" en object | null, et non en object. Ajoutez une vérification de null, sinon lire une propriété est une erreur (TS18047 'x' is possibly 'null'.).

typeof ne sait pas distinguer tableaux, dates, maps et objets simples : tous valent "object". Utilisez Array.isArray(x) pour les tableaux et x instanceof Date pour les instances de classes. Ni typeof ni quoi que ce soit d'autre ne peut vérifier à l'exécution qu'une valeur correspond à une interface, car les interfaces sont effacées ; pour cela, écrivez une fonction type guard qui vérifie les propriétés.

typeof en position de type

Dans un type, typeof someVariable copie le type que TypeScript a inféré (ou que vous avez déclaré) pour cette variable. Cela évite d'écrire à la main le type d'une valeur qui existe déjà, et garde les deux synchronisés.

const theme = {
  primary: "#3178c6",
  spacing: 8,
  dark: false,
};

type Theme = typeof theme;
// { primary: string; spacing: number; dark: boolean }

function withSpacing(t: Theme, factor: number): Theme {
  return { ...t, spacing: t.spacing * factor };
}

console.log(withSpacing(theme, 2)); // { primary: '#3178c6', spacing: 16, dark: false }

let userName = "Ada";
const fixed = "Ada";
type A = typeof userName; // string
type B = typeof fixed;    // "Ada" (a const keeps its literal type)

Le résultat dépend de la façon dont la variable a été déclarée : une let ou une propriété d'objet est élargie (string, number), une primitive const garde son type littéral. Ajoutez as const à un objet ou à un tableau et typeof donne des types littéraux readonly à tous les niveaux. typeof accepte aussi l'accès aux propriétés, typeof theme.spacing vaut number, mais pas les expressions arbitraires : type T = typeof getTheme(); est une erreur de syntaxe (';' expected., TS1005). Pour le type que renvoie un appel, utilisez ReturnType<typeof getTheme>.

keyof typeof : les clés d'un objet en tant que type

keyof typeof obj est la combinaison la plus courante. typeof transforme l'objet en type, keyof prend ses clés, et vous obtenez une union des noms de propriétés qui reste juste quand l'objet change.

La même astuce sur un tableau constant donne une union de ses valeurs : avec const roles = ["admin", "editor"] as const, le type (typeof roles)[number] vaut "admin" | "editor". Sans as const, ce ne serait qu'un string. La page keyof traite de keyof seul.

ReturnType<typeof fn> et typeof sur les classes

Les types utilitaires comme ReturnType et Parameters prennent un type de fonction. Un nom de fonction est une valeur : passez-le donc d'abord par typeof :

Écrire ReturnType<createUser> échoue avec 'createUser' refers to a value, but is being used as a type here. Did you mean 'typeof createUser'? (TS2749) : le compilateur demande exactement cela.

Les classes sont le seul cas où un nom est à la fois une valeur et un type. User comme type désigne une instance de la classe ; typeof User désigne la classe elle-même, le constructeur, avec ses membres statiques :

class Point {
  static origin = new Point(0, 0);
  constructor(public x: number, public y: number) {}
}

const p: Point = new Point(1, 2);        // an instance
const Ctor: typeof Point = Point;        // the class (constructor + statics)
const q = new Ctor(3, 4);                // q: Point
type Instance = InstanceType<typeof Point>; // Point

typeof, instanceof ou type guard

VérificationFonctionne surExemple
typeofprimitives, fonctions, « est-ce un objet »typeof x === "number"
Array.isArraytableauxArray.isArray(x)
instanceofinstances de classes (Date, Error, vos classes)x instanceof Date
inunions d'objets, par propriété"email" in x
Fonction type guardinterfaces, alias de type, tout le resteisUser(x) avec x is User

Choisissez l'outil le plus étroit qui convient. Pour les instances de classes, voir instanceof ; pour les formes décrites par une interface, une fonction type guard est la seule vérification possible à l'exécution.

Questions fréquentes

Que fait typeof en TypeScript ?

Deux choses différentes selon l'endroit où il apparaît. Dans une expression, typeof x est l'opérateur JavaScript : il s'exécute et renvoie une chaîne comme "string" ou "object", et TypeScript affine x quand vous comparez cette chaîne. Dans une annotation de type, typeof x est une requête de type : elle est évaluée par le compilateur, donne le type statique de la variable x, et disparaît du JavaScript émis.

Qu'est-ce que keyof typeof en TypeScript ?

keyof typeof obj donne l'union des noms de propriétés d'un objet sous forme de types littéraux de chaîne. typeof obj transforme la valeur objet en son type, et keyof prend les clés de ce type. Pour const colors = { red: "#f00", blue: "#00f" }, keyof typeof colors vaut "red" | "blue".

Pourquoi typeof null renvoie-t-il "object" ?

C'est un bug de la première version de JavaScript qu'on ne peut plus corriger sans casser le web. TypeScript le modélise : après typeof x === "object", x est affiné en object | null, vous devez donc aussi vérifier x !== null avant de lire des propriétés.

Comment vérifier qu'une valeur est un tableau en TypeScript ?

Utilisez Array.isArray(value), qui affine vers un type tableau. typeof ne peut pas le faire : typeof [] vaut "object", comme pour les objets simples et null.

Quelle est la différence entre typeof et instanceof en TypeScript ?

typeof vérifie la catégorie primitive d'une valeur ("string", "number", "function", "object"...). instanceof vérifie si un objet a été créé par une classe ou un constructeur particulier, comme Date ou votre propre class User. Utilisez typeof pour les primitives et instanceof pour les instances de classes ; aucun des deux ne peut vérifier une interface ou un alias de type, qui n'existent pas à l'exécution.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER