Menu

Signo de exclamación en TypeScript: la aserción non-null (!)

Un signo de exclamación después de un valor, como user!, es el operador de aserción non-null: quita null y undefined del tipo sin ninguna comprobación en tiempo de ejecución. Aprende qué hace x!, las formas de asignación definitiva let x!: T y prop!: T, por qué son arriesgadas y qué alternativas son más seguras.

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

Un signo de exclamación después de una expresión, value!, es el operador de aserción non-null. Quita null y undefined del tipo, así que un number | undefined se puede usar como un number. Es una promesa al compilador, no una comprobación: en tiempo de ejecución no pasa nada.

Sin el !, tea.toFixed(2) es el error TS18048, 'tea' is possibly 'undefined'. Con él, el código compila porque le dijiste al compilador que la clave existe.

Qué hace x! en tiempo de ejecución: nada

El ! se borra de la salida. El JavaScript compilado de prices.get("coffee")! es simplemente prices.get("coffee"). Si la aserción es errónea, el error aparece más tarde, en el primer sitio donde se usa el valor que falta:

El programa imprime Cannot read properties of undefined (reading 'toFixed'). El fallo ocurre en la línea que sigue al !, y en código real puede estar mucho más lejos: el undefined puede guardarse en un objeto y estallar en otro archivo. Esa distancia es la razón de que ! sea arriesgado.

Asignación definitiva: let x!: T

El mismo símbolo en una declaración significa algo relacionado. TypeScript registra si una variable se asigna antes de leerse, y no puede seguir una asignación hecha dentro de otra función:

index.ts(9,13): error TS2454: Variable 'config' is used before being assigned.

let config!: { port: number }; es una aserción de asignación definitiva: «esto se asignará antes de cualquier lectura». Arregla el error, con la misma pega que x!: si alguna vez se salta init(), la lectura obtiene undefined. Suele ser mejor reestructurar, por ejemplo const config = init(); con init devolviendo el objeto.

Propiedades de clase: prop!: T

Con strictPropertyInitialization (parte de strict), cada propiedad de clase debe inicializarse en su declaración o en el constructor. Si no, obtienes TS2564: Property 'socket' has no initializer and is not definitely assigned in the constructor. Cuando un método o un framework asigna la propiedad más tarde, prop!: T le dice al compilador que la acepte:

El primer console.log muestra el hueco: el tipo dice que socket siempre está ahí, pero antes de open() es undefined. Llamar a c.socket.send en ese momento compila y lanza un error. Si la propiedad de verdad puede faltar, declárala socket?: ... y compruébala, o crea el objeto en el constructor. El sitio principal donde prop! es práctica estándar es un framework que rellena una propiedad después de la construcción: el @ViewChild(...) child!: ChildDirective de Angular (asignado antes de que se ejecute ngAfterViewInit), o las clases de entidad de un ORM cuyas columnas rellena la librería al cargar una fila (la documentación de MikroORM escribe @Property() title!: string).

Alternativas más seguras

La mayoría de los ! se pueden sustituir por algo que el compilador comprueba, o por una comprobación que falla de forma clara en el sitio correcto:

Las dos funciones auxiliares estrechan el tipo igual que !, pero una suposición errónea produce missing HOST en el acto en lugar de un TypeError en otro sitio. assertDefined es una función de aserción (asserts value is ...): después de la llamada, el compilador trata host como un string. Hay más patrones en type guards.

En lugar deEscribeQué pasa cuando falta el valor
user!.nameif (user) { user.name }se salta el bloque
user!.nameuser?.nameundefined
count!count ?? 0se usa el valor por defecto
map.get(k)!must(map.get(k), "k")un error claro en esa línea
let x!: Tconst x = compute()no hay nada que pueda fallar

Los otros signos de exclamación

! significa cosas distintas según dónde esté:

CódigoSignificado
value! (después de una expresión)aserción non-null, solo TypeScript
let x!: T, prop!: Taserción de asignación definitiva, solo TypeScript
!value (antes de una expresión)NOT lógico, JavaScript normal
!!valueconvierte a booleano, JavaScript normal
a !== b, a != bdesigualdad, JavaScript normal

Solo los dos primeros se borran al compilar. !value y !!value se ejecutan en tiempo de ejecución y devuelven un booleano.

Preguntas frecuentes

¿Qué significa un signo de exclamación después de una variable en TypeScript?

value! es el operador de aserción non-null. Le dice al compilador que value no es null ni undefined, así que su tipo pierde esos dos miembros: string | undefined pasa a ser string. Se elimina del JavaScript generado y no añade ninguna comprobación en tiempo de ejecución, así que si te equivocas el programa falla más tarde con un TypeError.

¿Qué diferencia hay entre ! y ? en TypeScript?

x! afirma que el valor está presente y te da el tipo sin null, sin comprobar nada. x?.y comprueba en tiempo de ejecución: si x es null o undefined, se detiene y devuelve undefined. En una declaración, name?: string hace opcional una propiedad, mientras que name!: string dice que una propiedad obligatoria se asignará en algún sitio que el compilador no puede ver.

¿Qué significa let x!: string?

Es una aserción de asignación definitiva. Le dice al compilador que la variable se asignará antes de leerse, aunque el compilador no pueda demostrarlo (por ejemplo, porque la asignación ocurre dentro de otra función). Sin ella, leer la variable es el error TS2454, Variable 'x' is used before being assigned.

¿Cómo arreglo "has no initializer and is not definitely assigned in the constructor"?

Es el error TS2564 de strictPropertyInitialization. Da a la propiedad un valor inicial, asígnala en el constructor, hazla opcional (prop?: T) o, si un framework o un método de inicialización de verdad la asigna antes de usarla, escribe prop!: T. El ! es la última opción porque nada comprueba la promesa.

¿El operador de aserción non-null es una mala práctica?

No es un error, pero cada ! es una afirmación sin comprobar. Configuraciones de lint como @typescript-eslint/no-non-null-assertion lo señalan. Es preferible una comprobación que estreche (if (x), x ?? fallback, x?.y) o una función auxiliar que lance un error claro. Reserva ! para los sitios donde el valor está garantizado por una lógica que el compilador no puede seguir.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR