¿Cuál es la diferencia entre `any`, `unknown` y `never`? ¿Cuándo usarías cada uno?
Ver respuesta — intenta responderla en voz alta primero
any desactiva el chequeo de tipos: puedes hacer cualquier cosa y el compilador no dice nada. unknown es lo contrario: acepta cualquier valor, pero te obliga a verificar su tipo antes de usarlo. never representa algo que no debería ocurrir nunca: funciones que no retornan o ramas de código imposibles.
Los tres suenan parecidos pero cumplen roles opuestos:
any: apaga el compilador para ese valor. Puedes llamar métodos inexistentes y TypeScript no protesta. Además se contagia: todo lo que sale de una expresiónanytambién esany, y el agujero de tipos se extiende por el código.unknown: el "any seguro". Sirve para datos cuyo tipo no conoces todavía (respuestas de API,JSON.parse,catch). No puedes hacer nada con él hasta que hagas narrowing contypeof,instanceofo una validación.never: el tipo de "esto no debería pasar". Aparece en funciones que siempre lanzan un error y en la comprobación de exhaustividad de unswitch. Ningún valor puede asignarse anever, y eso es justo lo que lo hace útil.
Regla práctica: si te ves escribiendo any, casi siempre lo que quieres es unknown más una verificación.
// any: el compilador se apaga y el error llega en runtime
const datoAny: any = "hola";
datoAny.toFixed(2); // compila... y explota en runtime
const contagiado = datoAny + 1; // contagiado también es any
// unknown: acepta todo, pero exige verificar antes de usar
function procesarEntrada(entrada: unknown): string {
// entrada.toUpperCase();
// Error: 'entrada' is of type 'unknown'.
if (typeof entrada === "string") {
return entrada.toUpperCase(); // ya verificamos: aquí es string
}
if (typeof entrada === "number") {
return entrada.toFixed(2);
}
return "tipo no soportado";
}
// Caso real de unknown: JSON.parse devuelve any, mejor tratarlo como unknown
const crudo: unknown = JSON.parse('{"nombre":"Ana"}');
// never: funciones que jamás retornan
function fallar(mensaje: string): never {
throw new Error(mensaje);
}
// never en exhaustividad: garantiza que cubrimos todos los casos
type Rol = "admin" | "usuario";
function describirRol(rol: Rol): string {
switch (rol) {
case "admin": return "Acceso total";
case "usuario": return "Acceso limitado";
default: {
const nunca: never = rol; // si Rol crece, esto deja de compilar
return nunca;
}
}
}
Resolver un error de tipos con any "para que compile":
// El junior hace esto para silenciar el error:
const respuesta: any = await fetch("/api/usuario").then((r) => r.json());
console.log(respuesta.nombre.toUpperCase()); // puede explotar en runtime
// Mejor: unknown + verificación
const datos: unknown = await fetch("/api/usuario").then((r) => r.json());
if (typeof datos === "object" && datos !== null && "nombre" in datos) {
console.log(datos.nombre); // acceso seguro tras el narrowing
}
Con any el error no desaparece: solo se muda de la compilación al runtime, que es el peor lugar.
"
anydesactiva el chequeo de tipos por completo y además se contagia a todo lo que toca, así que evito usarlo.unknownes la alternativa segura: también acepta cualquier valor, pero el compilador me obliga a verificar el tipo antes de usarlo; es ideal para respuestas de API oJSON.parse. Yneverrepresenta lo imposible: el retorno de una función que siempre lanza un error, o el default de un switch exhaustivo, donde asigno el valor aneverpara que el compilador me avise si aparece un caso sin manejar."
Escribe longitud(valor: unknown): number que devuelva la longitud si valor es un string o un arreglo, y 0 en cualquier otro caso. Sin usar any ni as.
Ver solución
function longitud(valor: unknown): number {
if (typeof valor === "string") {
return valor.length; // narrowing a string
}
if (Array.isArray(valor)) {
return valor.length; // narrowing a arreglo
}
return 0;
}
longitud("hola"); // 4
longitud([1, 2, 3]); // 3
longitud(42); // 0