Evite usar any, use unknown en TypeScript

TypeScript es un lenguaje de programación fuertemente tipado y uno de los objetivos del sistema de tipos es hacer explícito la intención de código, con la finalidad de crear un código más robusto y consistente, pero si no usamos el sistemas de tipos adecuadamente podemos volver a caer el mismo infierno de JavaScript, siendo el tipo any uno de los más peligrosos si se usa sin una razón válida.

A una variable de tipo any se le puede asignar cualquier cosa, esto elimina las restricción de tipos, veamos algunos casos:

Caso 1

La variable country puede ser de cualquier tipo, string, number, boolean, etc, no es una variable fiable

let country: any = 'Perú'; // string 
country = 1;  // number 
country = false; // boolean 

Si usáramos un tipo definido, como por ejemplo country: string TypeScript nos alertaría con un mensaje, el cual diría algo parecido a esto, «El tipo ‘string’ no se puede asignar al tipo ‘number'». y no transpilaria el código hasta solventar el problema.

Caso 2

Usar any como tipo para los parámetros de una función (en este caso una función de suma), permitiría pasar cualquier cosa como parámetro, y en vez de sumar haría concatenaciones y estaríamos en la misma condición como si lo hicieras con JavaScript, ¡sin ninguna seguridad!

function sum(a: any, b: any) {
    return a + b
}

console.log(sum("Hello ", "World")) // "Hello World"
console.log(sum("Hello ",{a:10}))  // "Hello Object Object"

Caso 3

Definir un objeto como any nos da la libertad de acceder a cualquier atributo del objeto, ¡así no exista!

const book: any = {
  author: "author name",
  issn: "issn value"
};

console.log(book.doi) // undefined

Y así podríamos seguir, con muchos más ejemplos, de lo perjudicial que puede ser any , ¿pero qué tipo de datos podemos usar si no sabemos el tipo?, la respuesta es: unknown.

Si no conoce el valor de una variable, objeto, parámetro, etc, en el 99 % de los casos debería usar unknown en vez de any.

Unknown

Usar unknown nos permite trabajar con valores desconocidos con la seguridad de tipos , las dos principales características de unknown son:

  1. No permite que una variable definida como unknown pueda ser asignada a otra, evitando así la propagación de datos inconsistentes.
let x:unknown = 10; 

let v1: number =  x //  Error
let v2: object = x; // Error
let v3: string = x; // Error
let v4: string[] = x; // Error
let v5: {} = x; // Error
let v6: {} | null | undefined = x; // Error

// definimos el tipo 
let v10: number = x as number  // Ok

2. No permite realizar operaciones con el valor hasta definir un tipo específico.

function sum(a:unknown, b: number) {
    // comprobando el tipo
    if(typeof a === "number"){
        return a + b
    }
   throw new Error(`La variable '${a}' no es un número`);
    
}
sum(10,2) // 12

// Error 
sum("hola",2) // La variable 'hola' no es un número 
sum({a:5},2) // La variable [object Object] no es un número 

Si recibimos objetos que no sabemos su tipo, lo definimos como unknown para evitar que sea invocado directamente y haciendo uso de una interface o type podemos delimitar los atributos que queremos usar y si no está definido el atributo en nuestra interface o type TypeScript nos alertará.

// API y no conocemos su tipo  
const apiBook: unknown = {
  author: "author name",
  issn: "issn value"
   ....
};

interface book {
    author: string
    issn: string
}

// definimos el tipo book 
const x = apiBook as book;

console.log(x.author) // author name
console.log(apiBook.author) // Error, Object is of type 'unknown'.

Cuidarnos de any

TypeScript nos provee dentro del archivo de configuración tsconfig.json una opción donde podemos definir la regla noImplicitAny a true para que TypeScript nos alerte cada vez que se usa un tipo any.

{
    "compilerOptions": {
        ...
        "noImplicitAny": true
        ...
    }
}

O si quiere ir un paso más allá, (mi opción preferida y recomendada) es usar strict: true

{
  "compilerOptions": {
    ...
    "strict": true
    ...
  }
}

Esto habilitará las siguientes reglas:

  • noImplicitAny
  • noImplicitThis
  • strictNullChecks
  • alwaysStrict

Pero si hay una justificación válida para el uso de any y deseamos usarlo sin desactivar nuestra regla podemos usar // @ts-ignore, para ignorar todas las reglas.

// @ts-ignore
const myApiVar:any = {} // no mostrará ninguna alerta  

¿Cuales son los casos en que se puede usar any?

  1. Cuando hacemos migraciones de JavaScript a TypeScript, podemos marcar las variables como any para ir paso a paso y evitar de que se rompa todo.
  2. Cuando estamos usando librerías de terceros y no tenemos un tipo disponible para ese dato, ejemplo la librería nos devuelve un tipo de dato currency y ese tipo no podemos importarlo a nuestro proyecto o no podemos reproducirlo.

¿Que otro caso consideras que se justifica usar any?

Referencias

  1. https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-0.html#new-unknown-top-type
  2. https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#any
benjamin
Me llamo Benjamín Gonzales B, soy desarrollador de software con más de 15 años de experiencia, socio funduador de la empresa GNBIT. Me apasiona todo lo relacionado a las nuevas tecnologías, me gusta investigar , leer y aprender cada día algo nuevo. Desarrollo en PHP7+, JAVA, C#, JavaScript, entre otros y actualmente  estoy experimentando con lenguajes funcionales como: Erlang, Clojure y Scala 

2 Comments

Leave a Comment

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.