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 :
| Source | Type |
|---|---|
arr.find(...) | T | undefined |
map.get(key) | V | undefined |
propriété optionnelle p?: T | T | undefined à la lecture |
paramètre optionnel x?: T | T | 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érification | Retire du type |
|---|---|
x !== undefined | undefined |
x !== null | null |
x != null | null 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 gauche | left || "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 defindet deMap.get. - Acceptez
nulllà où une API vous le fournit :JSONn'a pas deundefined,String.prototype.matchet de nombreuses méthodes du DOM renvoientnull. - Vérifiez avec
== nullquand 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.