TypeScript y su eterna pregunta: ¿cómo hacer un tipo nullable?
\n\n¡Hola, colega! Si alguna vez has escrito en TypeScript, seguro te has encontrado con la situación en la que una función puede devolver un valor, o puede que no. O una variable no se inicializa de inmediato. Y aquí surge la pregunta sacramental: ¿cómo declarar un tipo que puede ser null o undefined?
Esta pregunta es una de las más populares en Stack Overflow (¡decenas de miles de visitas!), y no es casualidad. TypeScript es estricto, pero justo. No nos permite simplemente asignar null a una variable de tipo string. Y eso es maravilloso, porque nos salva de montones de errores. Pero, ¿qué hacer cuando nullable es parte de la lógica de negocio? Vamos a descubrirlo.
En este artículo no solo daremos una respuesta seca. Recorreremos desde lo básico hasta técnicas avanzadas, analizaremos errores comunes y aprenderemos a escribir código que sea seguro y elegante. ¡Allá vamos!
\n\nFundamentos: Union Types y Null
\n\nLa forma más simple y directa de hacer un tipo nullable es usar un union type (unión de tipos). Si una variable puede ser un string o null, escribimos así:
\n\nlet userName: string | null = null;\nuserName = "Alicia"; // Ok\nuserName = null; // También ok\n\n\nLo mismo funciona con undefined. En TypeScript, null y undefined son tipos diferentes (a menos que la bandera --strictNullChecks esté desactivada, pero hablaremos de eso más adelante).
let age: number | undefined;\nage = 25;\nage = undefined; // Funciona\n\n\nImportante: si usas | null o | undefined, TypeScript te obligará a verificar el valor antes de usarlo. Intentar llamar a un método en un objeto potencialmente null provocará un error de compilación. Esta es la "seguridad" por la que amamos TS.
Ejemplo de la vida real: búsqueda de usuario
\n\ninterface User {\n id: number;\n name: string;\n}\n\nfunction findUser(id: number): User | null {\n // ...lógica de búsqueda\n if (id === 1) {\n return { id: 1, name: "Iván" };\n }\n return null;\n}\n\nconst user = findUser(2);\n// user.name; // ¡Error! 'user' posiblemente es null\nif (user) {\n console.log(user.name); // Ahora es seguro\n}\n\n\n¿Ves? El compilador mismo te sugiere: "Oye, verifica, o te caerás". Esto ahorra horas de depuración.
\n\nParámetros y propiedades opcionales (Optional Chaining)
\n\nA menudo, no solo las variables son nullable, sino también los campos de objetos o los argumentos de funciones. Para esto, TypeScript tiene una sintaxis especial: el modificador opcional (el signo de interrogación ?).
Parámetros opcionales de función
\n\nfunction greet(name?: string) {\n if (name) {\n return `¡Hola, ${name}!`;\n }\n return "¡Hola, desconocido!";\n}\n\nconsole.log(greet("Pedro")); // ¡Hola, Pedro!\nconsole.log(greet()); // ¡Hola, desconocido!\n\n\nEl tipo del parámetro name se convierte automáticamente en string | undefined. Observa: si un parámetro es opcional, no es necesario pasarlo, y TS no se quejará.
Propiedades opcionales de interfaces
\n\ninterface Config {\n url: string;\n port?: number; // puede ser number o undefined\n}\n\nconst config1: Config = { url: "http://localhost" }; // port está ausente\nconst config2: Config = { url: "http://localhost", port: 3000 };\n\n\nAl acceder a config1.port, TypeScript dirá que es number | undefined. Y es correcto: no sabemos si el puerto está definido.
Encadenamiento opcional (Optional Chaining) — tu mejor amigo
\n\nCon la llegada de ES2020 y TypeScript 3.7, tenemos una forma elegante de acceder de forma segura a propiedades anidadas que pueden ser null o undefined. Esto se llama optional chaining