Menu

null y undefined en TypeScript: comprobaciones, ?. y ??

Con strictNullChecks, null y undefined son tipos separados que TypeScript te obliga a manejar. Aprende a comprobarlos, el encadenamiento opcional (?.), el doble signo de interrogación (??) y ??=, y la diferencia entre una propiedad opcional y | undefined.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Con strictNullChecks activado (forma parte de strict), null y undefined son tipos propios. Un string nunca puede ser null; un valor que puede faltar tiene que decirlo en su tipo, como string | null, y TypeScript te obliga a manejar ese caso antes de usar el valor.

Tras la comprobación, el compilador sabe que name es un string, así que se permite .split. El resto de esta página explica las formas de hacer esa comprobación y los operadores que la acortan.

strictNullChecks y los errores «possibly null»

Cuando un tipo incluye null o undefined, TypeScript se niega a usar el valor como si siempre estuviera ahí:

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

La versión con undefined es TS18048, 'x' is possibly 'undefined'. Sin strictNullChecks, null y undefined se permiten en todos los tipos y este código compila, y luego falla en tiempo de ejecución con un TypeError la primera vez que name es null. Ese tipo de bug es la razón principal para mantener strict activado.

De dónde salen estos tipos en el código de todos los días:

OrigenTipo
arr.find(...)T | undefined
map.get(key)V | undefined
propiedad opcional p?: TT | undefined al leerla
parámetro opcional x?: TT | undefined dentro de la función
str.match(re)RegExpMatchArray | null
JSON.parse(text)any, así que no se comprueba nada

Comprobar null y undefined

Cada una de estas comprobaciones estrecha el tipo dentro del bloque. Elige la que corresponda a lo que quieres excluir.

ComprobaciónQuita del tipo
x !== undefinedundefined
x !== nullnull
x != nullnull y undefined
typeof x !== "undefined"undefined
if (x)null y undefined, y además se salta los valores 0, "", false, NaN

== null es el único sitio donde la igualdad laxa es idiomática: es true exactamente para null y undefined, y para nada más. Una comprobación de veracidad habría tratado el string vacío como si faltara, lo que a menudo es un bug.

Encadenamiento opcional: ?.

a?.b lee b si a no es null ni undefined, y si lo es se detiene y devuelve undefined. El mismo operador funciona con índices, a?.[i], y con llamadas, fn?.().

El tipo de bob.address?.city es string | undefined: el encadenamiento opcional añade undefined al resultado, así que normalmente se combina con ??. El comportamiento en tiempo de ejecución es JavaScript normal; consulta encadenamiento opcional para los detalles del cortocircuito.

El doble signo de interrogación: ??

a ?? b devuelve a salvo que sea null o undefined, en cuyo caso devuelve b. Sustituye a la antigua expresión a || b, que además descarta 0, "", false y NaN:

Valor izquierdoleft || "d"left ?? "d"
null"d""d"
undefined"d""d"
0"d"0
"""d"""
false"d"false
NaN"d"NaN

En cuanto a los tipos, ?? quita null y undefined del lado izquierdo y une el resto con el lado derecho, y por eso scores.get("Linus") ?? 0 se puede asignar a un number.

Asignación nula: ??=

a ??= b asigna b a a solo cuando a es null o undefined. Sus hermanos ||= y &&= asignan cuando el lado izquierdo es falsy o truthy.

retries: 0 sobrevive a ??=, mientras que ||= sustituye el label vacío. Tras opts.retries ??= 3, TypeScript estrecha opts.retries a number en el resto de la función.

Propiedades opcionales frente a | undefined

nickname?: string y nickname: string | undefined se leen igual, pero se diferencian en si la clave tiene que existir:

Usa ? cuando quien llama pueda omitir la propiedad, y | undefined cuando quieras que todos la pasen de forma explícita, aunque el valor sea undefined. La opción exactOptionalPropertyTypes (no forma parte de strict) endurece más ?: nickname?: string rechaza entonces { nickname: undefined } y solo acepta una clave ausente o un string. Los parámetros opcionales de las funciones (x?: number) se comportan como las propiedades opcionales: dentro de la función x es number | undefined.

null o undefined: cuál usar

TypeScript no obliga a elegir, pero mezclar los dos en un mismo proyecto significa que cada comprobación tiene que manejar dos casos. Una convención habitual:

  • Usa undefined (y propiedades opcionales) para «no asignado» en tus propios tipos. Es lo que JavaScript produce por defecto: propiedades que no existen, argumentos omitidos, find y Map.get sin resultado.
  • Acepta null donde te lo da una API: JSON no tiene undefined, y String.prototype.match y muchos métodos del DOM devuelven null.
  • Comprueba con == null cuando un valor pueda ser cualquiera de los dos.

La indexación de arrays es el único hueco: users[5] tiene el tipo de los elementos aunque el índice esté fuera de rango. La opción noUncheckedIndexedAccess (no forma parte de strict) añade | undefined a cada acceso por índice para que el compilador también lo detecte.

Preguntas frecuentes

¿Qué significa el doble signo de interrogación en TypeScript?

a ?? b es el operador de fusión nula (nullish coalescing) de JavaScript. Devuelve a salvo que a sea null o undefined, en cuyo caso devuelve b. A diferencia de ||, conserva otros valores falsy como 0, "" y false. TypeScript quita null y undefined del tipo del lado izquierdo, así que cuando a es string | undefined, a ?? "x" es un string.

¿Cómo compruebo si un valor es undefined en TypeScript?

Compáralo: if (value !== undefined) { ... }. TypeScript estrecha el tipo dentro del bloque. Para descartar a la vez null y undefined, usa value != null (igualdad laxa), que es el único sitio donde ==/!= es idiomático. Una comprobación de veracidad (if (value)) también estrecha, pero se salta 0, "" y false.

¿Qué significa "Object is possibly undefined"?

Los errores TS18048 ('x' is possibly 'undefined') y TS18047 ('x' is possibly 'null'), o TS2532 (Object is possibly 'undefined') cuando el valor no tiene un nombre sencillo, como en getUser().address.city, vienen de strictNullChecks: el tipo incluye undefined o null, y el código usa el valor como si no pudiera serlo. Compruébalo antes, usa el encadenamiento opcional (x?.name), da un valor por defecto con ??, o cambia el tipo si el valor de verdad no puede faltar.

¿Qué diferencia hay entre null y undefined en TypeScript?

Son dos tipos separados con un valor cada uno. JavaScript usa undefined para lo que nunca se asignó (una propiedad que no existe, un argumento omitido, Map.get con una clave que no está) y las APIs usan null para un «sin valor» intencionado (JSON, muchos métodos del DOM). TypeScript los registra por separado, así que string | null no acepta undefined. Muchos proyectos eligen undefined para su propio código y solo aceptan null en las fronteras.

¿Una propiedad opcional es lo mismo que | undefined?

No exactamente. name?: string significa que la propiedad puede faltar por completo, y leerla da string | undefined. name: string | undefined significa que la propiedad debe estar presente, aunque su valor sea undefined. Con exactOptionalPropertyTypes activado, name?: string también deja de aceptar un valor undefined explícito.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR