Menu

null et undefined en TypeScript : vérifications, ?. et ??

Avec strictNullChecks, null et undefined sont des types distincts que TypeScript vous oblige à traiter. Découvrez comment les vérifier, le chaînage optionnel (?.), le double point d'interrogation (??) et ??=, et la différence entre une propriété optionnelle et | undefined.

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

Avec strictNullChecks activé (l'option fait partie de strict), null et undefined sont des types à part entière. Un string ne peut jamais valoir null ; une valeur qui peut manquer doit l'indiquer dans son type, comme string | null, et TypeScript vous oblige à traiter ce cas avant d'utiliser la valeur.

Après la vérification, le compilateur sait que name est un string, donc .split est autorisé. La suite de cette page présente les façons de faire cette vérification et les opérateurs qui la raccourcissent.

strictNullChecks et les erreurs « possibly null »

Quand un type inclut null ou undefined, TypeScript refuse d'utiliser la valeur comme si elle était toujours présente :

index.ts(3,22): error TS18047: 'name' is possibly 'null'.

La version undefined est TS18048, 'x' is possibly 'undefined'. Sans strictNullChecks, null et undefined sont autorisés dans tous les types : ce code compile, puis plante à l'exécution avec une TypeError la première fois que name vaut null. Cette catégorie de bugs est la principale raison de garder strict activé.

D'où viennent ces types dans le code de tous les jours :

SourceType
arr.find(...)T | undefined
map.get(key)V | undefined
propriété optionnelle p?: TT | undefined à la lecture
paramètre optionnel x?: TT | undefined dans la fonction
str.match(re)RegExpMatchArray | null
JSON.parse(text)any, donc rien n'est vérifié

Vérifier null et undefined

Chaque vérification ci-dessous affine le type dans le bloc. Choisissez celle qui correspond à ce que vous voulez exclure.

VérificationRetire du type
x !== undefinedundefined
x !== nullnull
x != nullnull et undefined
typeof x !== "undefined"undefined
if (x)null et undefined, et écarte aussi les valeurs 0, "", false, NaN

== null est le seul endroit où l'égalité large est idiomatique : elle est vraie exactement pour null et undefined, rien d'autre. Un test de véracité aurait traité la chaîne vide comme absente, ce qui est souvent un bug.

Le chaînage optionnel : ?.

a?.b lit b si a ne vaut ni null ni undefined, et sinon s'arrête et renvoie undefined. Le même opérateur fonctionne pour les indices, a?.[i], et pour les appels, fn?.().

Le type de bob.address?.city est string | undefined : le chaînage optionnel ajoute undefined au résultat, on l'associe donc en général à ??. Le comportement à l'exécution est du JavaScript pur ; voir chaînage optionnel pour le détail du court-circuit.

Le double point d'interrogation : ??

a ?? b renvoie a sauf s'il vaut null ou undefined, auquel cas il renvoie b. Il remplace l'ancien idiome a || b, qui écarte aussi 0, "", false et NaN :

Valeur de gaucheleft || "d"left ?? "d"
null"d""d"
undefined"d""d"
0"d"0
"""d"""
false"d"false
NaN"d"NaN

Côté types, ?? retire null et undefined du côté gauche et fait l'union du reste avec le côté droit, c'est pourquoi scores.get("Linus") ?? 0 peut être assigné à un number.

L'assignation nullish : ??=

a ??= b assigne b à a uniquement quand a vaut null ou undefined. Ses cousins ||= et &&= assignent quand le côté gauche est falsy ou truthy.

retries: 0 survit à ??=, alors que le label vide est remplacé par ||=. Après opts.retries ??= 3, TypeScript affine opts.retries en number pour le reste de la fonction.

Propriétés optionnelles ou | undefined

nickname?: string et nickname: string | undefined se lisent de la même façon, mais diffèrent sur l'obligation de présence de la clé :

Utilisez ? quand les appelants peuvent omettre la propriété, et | undefined quand vous voulez que chaque appelant la passe explicitement, même si la valeur est undefined. L'option exactOptionalPropertyTypes (qui ne fait pas partie de strict) resserre encore ? : nickname?: string refuse alors { nickname: undefined } et n'accepte qu'une clé absente ou une chaîne. Les paramètres de fonction optionnels (x?: number) se comportent comme les propriétés optionnelles : dans la fonction, x est number | undefined.

null ou undefined : lequel utiliser

TypeScript n'impose pas de choix, mais mélanger les deux dans une base de code oblige chaque vérification à traiter deux cas. Une convention courante :

  • Utilisez undefined (et les propriétés optionnelles) pour « non défini » dans vos propres types. C'est ce que produit JavaScript par défaut : propriétés absentes, arguments omis, échecs de find et de Map.get.
  • Acceptez null là où une API vous le fournit : JSON n'a pas de undefined, String.prototype.match et de nombreuses méthodes du DOM renvoient null.
  • Vérifiez avec == null quand une valeur peut être l'un ou l'autre.

L'accès aux tableaux par indice est la seule lacune : users[5] est typé comme le type des éléments même quand l'indice est hors limites. L'option noUncheckedIndexedAccess (qui ne fait pas partie de strict) ajoute | undefined à chaque accès par indice pour que le compilateur détecte aussi ce cas.

Questions fréquentes

Que signifie le double point d'interrogation en TypeScript ?

a ?? b est l'opérateur de coalescence des nuls (nullish coalescing) de JavaScript. Il renvoie a sauf si a vaut null ou undefined, auquel cas il renvoie b. Contrairement à ||, il conserve les autres valeurs falsy comme 0, "" et false. TypeScript retire null et undefined du type du côté gauche : quand a est string | undefined, a ?? "x" est un string.

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

Comparez-la : if (value !== undefined) { ... }. TypeScript affine le type dans le bloc. Pour écarter à la fois null et undefined en une seule vérification, utilisez value != null (égalité large), le seul endroit où ==/!= est idiomatique. Un test de véracité (if (value)) affine aussi, mais écarte 0, "" et false.

Que signifie « Object is possibly undefined » ?

Les erreurs TS18048 ('x' is possibly 'undefined') et TS18047 ('x' is possibly 'null'), ou TS2532 (Object is possibly 'undefined') quand la valeur n'a pas de nom simple, comme dans getUser().address.city, viennent de strictNullChecks : le type inclut undefined ou null, et le code utilise la valeur comme si elle ne pouvait pas l'être. Vérifiez-la d'abord, utilisez le chaînage optionnel (x?.name), fournissez une valeur par défaut avec ??, ou changez le type si la valeur ne peut vraiment pas manquer.

Quelle est la différence entre null et undefined en TypeScript ?

Ce sont deux types distincts qui ont chacun une seule valeur. JavaScript utilise undefined pour ce qui n'a jamais été défini (une propriété absente, un argument omis, Map.get sur une clé absente) et les API utilisent null pour une absence de valeur volontaire (JSON, de nombreuses méthodes du DOM). TypeScript les suit séparément, donc string | null n'accepte pas undefined. Beaucoup de bases de code choisissent undefined pour leur propre code et n'acceptent null qu'aux frontières.

Une propriété optionnelle équivaut-elle à | undefined ?

Pas tout à fait. name?: string signifie que la propriété peut être totalement absente, et la lire donne string | undefined. name: string | undefined signifie que la propriété doit être présente, même si sa valeur est undefined. Avec exactOptionalPropertyTypes activé, name?: string n'accepte plus non plus une valeur undefined explicite.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER