cover

Aclarando las confusiones: Any vs Unknown en TypeScript

author photo

Héctorbliss

@hectorbliss


robot logo saying hello

No te quedes atrás: Actualízate

Suscríbete para recibir información sobre nuevos frameworks, updates, eventos, tips, hacks y más.

TypeScript es un lenguaje de programación que se ha vuelto extremadamente popular en los últimos años. Sobre todo por la seguridad que agrega a nuestros programas, haciendonos programar con más confianza y menos "debugging".

Uno de los aspectos más importantes de TypeScript es la capacidad de definir tipos, lo que ayuda a evitar errores y problemas en tiempo de ejecución. Sin embargo, a menudo se presentan confusiones cuando se trata de utilizar los tipos "any" y "unknown".

En este post, Te voy a explicar cuáles son las diferencias entre ambos:

En Resumen: (TL;DR)

Aunque "any" y "unknown" se usan para manejar variables de tipos desconocidos, hay diferencias importantes entre ambos. En general, se recomienda utilizar "unknown" en la mayoría de los casos, ya que obliga al desarrollador a realizar una validación completa en tiempo de compilación. Sin embargo, si no hay otra opción, "any" puede ser utilizado, pero siempre es importante tener en cuenta sus posibles riesgos y desventajas.

¿Qué es "any"?

"Any" se utiliza cuando no se conoce el tipo de una variable o cuando se necesita ignorarlo. "any" Significa cualquier tipo de dato.

La ventaja de "any" es que permite asignar cualquier valor a una variable sin que TypeScript realice comprobaciones de tipo en tiempo de ejecución.

En TypeScript, todos los tipos son asignables a "any". Esto hace a "any" un "top type" también conocido como "universal supertype" del sistema de tipos WIKI.

let value: any; value = true; // OK value = 42; // OK value = "Hola Blissmo"; // OK value = []; // OK value = {}; // OK value = Math.random; // OK value = null; // OK value = undefined; // OK value = new TypeError(); // OK value = Symbol("type"); // OK

El tipo "any" es esencialmente un atajo, una salida rápida para escapar del sistema de tipos.

En muchas ocasiones, esto es demasiado permisivo. Usando el tipo "any" es fácil deshacerse de la linterna, pero nuestro código estará desprotegido en "runtime".

Aunque "any" puede parecer una solución rápida y fácil, no es recomendable emplearlo ya. Puedes configurar en tu archivo de TS para que tus programas no lo acepten más, con el siguiente flag:

{ "noImplicitAny": true, // o también: "strict": true, }

¿Qué es "unknown"?

Pero existe un "top type" que es seguro por default. Este tipo es "unknown" que fue introducido en la versión 3.0 de TS y fue creado por Anders Hejlsberg para ser la contraparte de "any".

A diferencia de "any", "unknown" no permite asignar cualquier tipo de valores a una variable sin explicitar el tipo. De hecho, el compilador de TypeScript obliga a estar seguro antes de asignar cualquier valor a una variable con el tipo "unknown".

let value: unknown; value = true; // OK value = 42; // OK value = "Hello World"; // OK value = []; // OK value = {}; // OK value = Math.random; // OK value = null; // OK value = undefined; // OK value = new TypeError(); // OK value = Symbol("type"); // OK

Todas las asignaciones a la variable value se consideran "type-correct". Pero, ¿Qué pasa si se quiere asignar una variable de tipo "unknown" a variables de otros tipos?

let value: unknown; let value1: unknown = value; // OK let value2: any = value; // OK let value3: boolean = value; // Error let value4: number = value; // Error let value5: string = value; // Error let value6: object = value; // Error let value7: any[] = value; // Error let value8: Function = value; // Error

El tipo "unknown" es asignable solamente al tipo "any" y al propio "unknown". Esto hace más sentido que lo que teníamos con "any".

Esta es justamente la principal propuesta de valor del tipo "unknown". TypesScript no nos va a dejar realizar ninguna operación arbitraria en valores de tipo "unknown". Deberemos hacer algún tipo de chequeo antes de trabajar con la variable. Esta es la principal diferencia entre estos tipos.

Ejemplos en el uso de unknown

function convertToString(value: unknown): string { if (typeof value === "function") { const functionName = value.name || "anonymous"; return `[function ${functionName}]`; } if (value instanceof Date) { return value.toISOString(); } return String(value); }

Toma nota como al no conocer el tipo de value, se debe hacer los chequeos correspondientes para poder ejecutar la operación correcta.

const value: unknown = "Hello World"; const someString: string = value as string; const otherString = someString.toUpperCase(); // "HELLO WORLD"

En este ejemplo, en la segunda línea, el "type-checker" confía en que tú, como developer, sabes los que haces y acepta tu indicación. Pero esto no es una comprobación y el programa es susceptible a fallar.

type UnionType1 = unknown | null; // unknown type UnionType2 = unknown | undefined; // unknown type UnionType3 = unknown | string; // unknown type UnionType4 = unknown | number[]; // unknown

También es importante denotar que cualquier union con "unknown" será evaluada a "unknown" por default.

type UnionType5 = unknown | any; // any

Excepto por "any". Lo que vuelve a any top-type, sobre "unknown".

¿Cuáles son las diferencias entre "any" y "unknown"?

La principal diferencia entre "any" y "unknown" es que "any" permite cualquier cosa, mientras que "unknown" no permite nada.

"unknown", por lo tanto, obliga al desarrollador a realizar un tipo seguro antes de asignar cualquier valor a la variable.

Otra diferencia importante es que "any" inhibe la capacidad de efectuar una validación en tiempo de ejecución, lo que puede llevar a errores y problemas imprevistos, mientras que con "unknown" cualquier error es detectado antes de ejecutarse el código.

¿Cuándo utilizar "any" y "unknown"?

La regla general es que si no hay otra opción, "any" se usa para tratar con datos de tipos desconocidos. Sin embargo, es crucial tener en cuenta que "any" puede ser potencialmente peligroso en términos de seguridad, ya que las operaciones en estas variables pueden tener errores que no son detectados en tiempo de ejecución.

Por otro lado, "unknown" debería ser la elección por defecto si se desea trabajar con variables de tipos desconocidos.

A menudo, se usa "unknown" para manejar entradas externas, como resultados de solicitudes de API, que requieren una validación adicional antes de ser usadas.

EJEMPLO REAL:

type Result = | { ok: true; value: unknown } | { ok: false; error: Error }; function getLocalStorageItem(key:string):Result { const item = localStorage.getItem(key); if(!item) return { ok: false, error: new Error(`${key} doesn't exist`)}; let value: unknown; try { value = JSON.parse(item); return { ok: true, value } // seguimos sin saber el tipo, pero sabemos que existe } catch (error) { return { ok:false, error } } }

¡Ojo! 👀 Result es un "tagged union type" o "discriminated union type" también conocido como Maybe, Option u Optional. Este tipo de combinación es muy util para retornar una respuesta de error "segura" que no rompe el programa. He de confesar que la inspiración ha sido Zod 100%.

Cualquier función que use nuestro getLocalStorageItem deberá comprobar el tipo de value:

const result = getLocalStorageItem('theme'); if(result.ok) { const theme: unknown = result.value; if(typeof theme === 'string' && (theme==='dark' || theme==='light')) { console.log("Theme applied"); setTheme(theme); } else { console.error(result.error); } }

¡Y ya está! Recuerda usar "unknown" que es un tipo válido para tus programas cuando genuinamente es poco probable saber el tipo de dato, mientras que "any" es solo una salida fácil 😉

Abrazo. Bliss.

banner

¿Quieres mantenerte al día sobre los próximos cursos y eventos?

Suscríbete a nuestro newsletter

Jamás te enviaremos spam, nunca compartiremos tus datos y puedes cancelar tu suscripción en cualquier momento 😉

robot logo saying hello
facebook icontwitter iconlinkedin iconinstagram iconyoutube icon

© 2016 - 2023 Fixtergeek