Un type guard es una comprobación en tiempo de ejecución que TypeScript entiende, así que estrecha el tipo dentro de la rama comprobada. typeof, instanceof e in son guards integrados; para cualquier otra cosa, escribes una función cuyo tipo de retorno es un predicado de tipo, value is Type.
isUser devuelve un booleano normal en tiempo de ejecución. El tipo de retorno value is User le dice al compilador qué demuestra un resultado true, y cada if (isUser(x)) estrecha entonces x a User.
Type guards integrados
Estas comprobaciones estrechan sin ninguna función auxiliar:
| Guard | Ejemplo | Úsalo para |
|---|---|---|
typeof | typeof x === "number" | primitivos y funciones |
instanceof | x instanceof Date | instancias de clases |
in | "email" in x | uniones de objetos, propiedades de objetos unknown |
Array.isArray | Array.isArray(x) | arrays |
| Igualdad | x === null, x.kind === "circle" | null/undefined, etiquetas literales |
| Veracidad | if (x) | quitar null y undefined |
Todas se ejecutan como JavaScript normal. Lo que añade TypeScript es el estrechamiento: lee la comprobación y ajusta el tipo en cada rama. La lista completa de formas está en la página de estrechamiento de tipos. Un guard propio sirve para comprobaciones que no caben en una sola expresión o que quieres reutilizar.
Escribir un predicado de tipo
Un predicado de tipo tiene la forma parameterName is Type y sustituye a boolean como tipo de retorno. El estrechamiento funciona en los dos sentidos: true estrecha a Type, y false quita Type de una unión.
Pasar un guard a filter da un array con el tipo correcto. Desde TypeScript 5.5 el compilador también infiere un predicado a partir de funciones flecha sencillas, así que pets.filter((p) => p.kind === "cat") devuelve Cat[] sin un guard con nombre.
El tipo del predicado tiene que encajar con el tipo del parámetro: function f(x: string): x is number es el error TS2677, A type predicate's type must be assignable to its parameter's type.
El compilador se fía de tu guard
TypeScript comprueba que un guard devuelve un booleano. No comprueba que ese booleano sea correcto. Un guard que devuelve true para valores equivocados hace que los tipos mientan, y el programa falla en tiempo de ejecución sin ningún error de compilación.
data.price.toFixed(2) lanza TypeError: Cannot read properties of undefined (reading 'toFixed') en tiempo de ejecución. El compilador aceptó data.price como number porque el guard lo decía. Comprueba cada propiedad de la que depende el resto del código, y mantén los guards pequeños, probados y cerca del tipo que describen.
Comprobar si un objeto es de un tipo
Esta es la pregunta detrás de la mayoría de los guards propios: los datos llegan como unknown (de JSON.parse, fetch, localStorage, un mensaje) y necesitas saber si encajan con tu interfaz. La receta:
typeof value === "object" && value !== null(un objeto, nonull)."prop" in valuepara cada propiedad obligatoria. Sobreunknown,inañade la propiedad al tipo comounknown.typeof value.prop === "..."(o un guard anidado) para el tipo de cada propiedad.Array.isArray(value.items) && value.items.every(isItem)para los arrays.
Con formas grandes o muy anidadas, escribir esto a mano se vuelve pesado. Librerías de esquemas como Zod o Valibot te permiten describir la forma una vez y te dan tanto la comprobación en tiempo de ejecución como el tipo de TypeScript.
Funciones de aserción: asserts value is Type
Una función de aserción lanza un error si la comprobación falla y termina con normalidad en caso contrario. Su tipo de retorno es asserts value is Type (o asserts condition), y todo lo que va después de la llamada queda estrechado, sin necesidad de if.
Hay una regla que confunde a mucha gente: una función de aserción debe llamarse a través de un nombre con un tipo explícito. Una función flecha const sin anotación, const check = (v: unknown): asserts v is string => {...}, da el error TS2775 en la llamada, Assertions require every name in the call target to be declared with an explicit type annotation. Usa una declaración function, o anota la constante con un tipo de función.
Guards frente a aserciones y casts
| Herramienta | ¿Comprueba en ejecución? | Estrecha | Si falla |
|---|---|---|---|
Guard integrado (typeof, in...) | Sí | dentro de la rama | va por la otra rama |
Función value is T | Sí (tu código) | dentro de la rama | va por la otra rama |
Función asserts value is T | Sí (tu código) | después de la llamada | lanza un error |
value as T | No | la expresión | nada: el tipo equivocado se propaga |
Una aserción de tipo (as) cambia el tipo sin comprobar nada. En una frontera por la que entran datos de fuera, un guard o una función de aserción es la versión segura de la misma idea.
Guards basados en this en clases
Un método puede estrechar el objeto sobre el que se llama con this is Type. Es práctico en jerarquías de clases:
class FileNode {
constructor(public name: string) {}
isDirectory(): this is DirectoryNode {
return this instanceof DirectoryNode;
}
}
class DirectoryNode extends FileNode {
children: FileNode[] = [];
}
function count(node: FileNode): number {
return node.isDirectory() ? node.children.length : 0; // node: DirectoryNode in the true branch
}
Preguntas frecuentes
¿Qué es un type guard en TypeScript?
Cualquier comprobación en tiempo de ejecución que TypeScript usa para estrechar un tipo: typeof x === "string", x instanceof Date, "id" in x, Array.isArray(x), o la llamada a una función cuyo tipo de retorno es un predicado de tipo como x is User. Dentro de la rama comprobada, la variable tiene el tipo más estrecho.
¿Cómo compruebo si un objeto es de un tipo en TypeScript?
Los tipos no existen en tiempo de ejecución, así que compruebas las propiedades: escribe una función isUser(value: unknown): value is User que compruebe typeof value === "object", value !== null y cada propiedad obligatoria con in y typeof. Tras if (isUser(x)), x tiene tipo User. Para clases, basta con x instanceof MyClass.
¿Qué significa "value is Type" en TypeScript?
Es un predicado de tipo, que se usa como tipo de retorno de una función. La función sigue devolviendo un booleano en tiempo de ejecución, pero cuando devuelve true TypeScript estrecha el argumento a Type en la llamada, y cuando devuelve false lo estrecha a los demás miembros de la unión. El compilador no verifica el cuerpo de la función, así que la comprobación tiene que ser correcta.
¿Qué diferencia hay entre un type guard y una función de aserción?
Un type guard (x is T) devuelve un booleano y estrecha dentro de un if. Una función de aserción (asserts x is T) no devuelve nada y lanza un error cuando la comprobación falla, así que todo lo que va después de la llamada queda estrechado sin necesidad de if. Usa guards para ramificar y aserciones para "esto tiene que cumplirse o se para todo".
¿Puedo comprobar si un objeto implementa una interfaz en TypeScript?
No directamente: las interfaces se borran e instanceof no las acepta. Escribe un type guard que compruebe las propiedades de la interfaz, o añade una propiedad etiqueta literal (kind: "user") y compárala. Librerías de esquemas como Zod generan la comprobación y el tipo a partir de una sola definición.