any, unknown, never y void en TypeScript | Nicolás Garzón
Texto
Copiar any → desactiva comprobación
unknown → cualquier valor, pero todavía no utilizable
never → ningún valor posible
void → el retorno no ofrece un valor útil al consumidor
TypeScript
Copiar let value: any = "hello" ;
value. nonExisting. method ( ) ;
value = 10 ;
const product: Product = value; any permite casi cualquier operación y se propaga hacia otros tipos.
No significa únicamente “puede contener cualquier valor”; unknown también puede. La diferencia es que any evita que el checker te proteja.
Librerías sin declaraciones.
JavaScript heredado.
JSON.parse.
Parámetros implícitos cuando las reglas lo permiten.
Assertions o casts innecesarios.
APIs genéricas mal tipadas.
TypeScript
Copiar const value = JSON . parse ( text) ; El resultado histórico es any, por lo que conviene asignarlo inmediatamente a unknown o pasar por un parser.
TypeScript
Copiar const value: unknown = JSON . parse ( text) ; TypeScript
Copiar const user: any = loadUser ( ) ;
const name = user. profile. name; name también termina como any. Los errores pueden recorrer varias capas sin diagnóstico.
Encapsula una frontera legacy y devuelve un contrato seguro.
TypeScript
Copiar let value: unknown = await loadValue ( ) ; Puede recibir cualquier cosa:
TypeScript
Copiar value = "text" ;
value = 10 ;
value = null ; Pero no puedes usarla sin reducir posibilidades:
TypeScript
Copiar value. toUpperCase ( ) ; TypeScript
Copiar if ( typeof value === "string" ) {
console . log ( value. toUpperCase ( ) ) ;
} Dentro de la rama, TypeScript conoce string.
TypeScript
Copiar try {
await saveProduct ( ) ;
} catch ( error: unknown ) {
if ( error instanceof Error ) {
console . error ( error. message) ;
}
} JavaScript permite lanzar cualquier valor:
JavaScript
Copiar throw "failed" ;
throw null ;
throw { code : "FAILED" } ; Por eso asumir Error sin comprobar no es seguro.
Texto
Copiar acepta cualquier valor permite cualquier uso
any sí sí
unknown sí noUtiliza unknown en fronteras donde todavía debes demostrar qué recibiste.
TypeScript
Copiar function fail ( message: string ) : never {
throw new Error ( message) ;
} La función no produce un valor normal.
También puede no terminar:
TypeScript
Copiar function runForever ( ) : never {
while ( true ) { }
} Si un union pierde todas sus posibilidades después del narrowing, el tipo resultante es never.
TypeScript
Copiar type Status = "idle" | "loading" ;
function describe ( status: Status) {
if ( status === "idle" ) return "Idle" ;
if ( status === "loading" ) return "Loading" ;
status;
} TypeScript
Copiar type Result =
| { status: "success" ; value: string }
| { status: "error" ; error: Error } ;
function render ( result: Result) : string {
switch ( result. status) {
case "success" :
return result. value;
case "error" :
return result. error. message;
default :
return assertNever ( result) ;
}
}
function assertNever ( value: never ) : never {
throw new Error ( ` Unexpected value: ${ String ( value) } ` ) ;
} Si se añade otra variante, el default deja de recibir never y aparece un error.
TypeScript
Copiar type Value = string | never ; TypeScript
Copiar type Value = string ; Añadir “ningún valor posible” no añade una posibilidad nueva.
TypeScript
Copiar function logMessage ( message: string ) : void {
console . log ( message) ;
} La función puede terminar normalmente, pero el contrato no entrega un valor útil.
TypeScript
Copiar function returnsUndefined ( ) : undefined {
return undefined ;
} Este contrato exige el valor undefined.
TypeScript
Copiar function returnsVoid ( ) : void {
return ;
} void comunica que el retorno debe ignorarse.
TypeScript
Copiar const numbers = [ 1 , 2 , 3 ] ;
const result = [ ] as number [ ] ;
numbers. forEach ( ( value) => result. push ( value) ) ; push retorna un número, pero forEach espera una callback cuyo resultado se ignora.
Una función que devuelve un valor puede ser compatible con () => void en muchos contextos porque el consumidor promete no utilizar ese retorno.
TypeScript
Copiar const handler : ( ) => void = ( ) => {
return 10 ;
} ; La llamada se ve como void:
TypeScript
Copiar const result = handler ( ) ; El valor puede existir en JavaScript, pero el tipo impide depender de él mediante ese contrato.
Esto no significa que una función declarada explícitamente con retorno void pueda retornar cualquier expresión en todas las formas. La regla de compatibilidad de callbacks y la anotación de implementación tienen contextos distintos.
Texto
Copiar void → termina, pero no proporciona un valor útil
never → no alcanza una finalización normalTypeScript
Copiar function log ( ) : void {
console . log ( "done" ) ;
}
function fail ( ) : never {
throw new Error ( "failed" ) ;
} TypeScript
Copiar function requireValue ( value: string | null ) : string {
if ( value === null ) {
throw new Error ( "Value is required" ) ;
}
return value;
} El retorno general es string, no never, porque algunas rutas terminan con un valor.
TypeScript
Copiar function migrate ( value: any ) : Product {
return value;
} El retorno Product parece seguro, pero no se comprobó nada.
TypeScript
Copiar function migrate ( value: unknown ) : Product {
return parseProduct ( value) ;
} TypeScript
Copiar function getErrorMessage ( error: unknown ) : string {
if ( error instanceof Error ) {
return error. message;
}
if ( typeof error === "string" ) {
return error;
}
return "Unknown error" ;
} La función convierte una frontera abierta en un string estable para la UI.
Utilizar any y creer que conserva seguridad.
Propagar any desde una frontera externa.
Acceder a unknown sin narrowing.
Asumir que catch siempre recibe Error.
Confundir never con void.
Escribir string | never esperando representar un error.
Depender del retorno de una callback tipada como void.
Utilizar assertions para convertir unknown sin validar.
Declarar never en una función que sí puede terminar.
any desactiva comprobación y se propaga.
unknown acepta todo, pero exige demostrar antes de usar.
never representa ausencia de valores posibles.
void indica que el consumidor ignora el retorno.
JavaScript puede lanzar cualquier valor.
never permite comprobar exhaustividad.
Una callback compatible con void puede retornar un valor que será ignorado.
Las fronteras externas deberían empezar como unknown.
¿Por qué unknown es más seguro que any para una respuesta externa?
Respuesta Porque ambos pueden contener cualquier valor, pero unknown obliga a comprobar su forma antes de acceder a propiedades u operaciones; any desactiva ese control.
null, undefined y ausencia explica cómo TypeScript diferencia un valor vacío, una propiedad ausente y una posibilidad todavía no comprobada.