Lecciones del módulo (2/2)
Uniones y Estrechamiento
En el mundo real, las variables y las respuestas de las APIs no siempre tienen un único tipo fijo. TypeScript ofrece los Tipos Unión (Union Types) para gestionar la variabilidad, y el Estrechamiento de Tipos (Type Narrowing) para operar de forma segura con ellos en tiempo de ejecución.
Tipos Unión (Union Types)
Un tipo unión permite que una variable acepte valores de diferentes tipos. Se expresa mediante el símbolo de la barra vertical (|):
let result: number | string;
result = 42; // Valido
result = 'Errore 404'; // ValidoSin embargo, cuando trabajamos con un tipo unión, no podemos llamar directamente a métodos que pertenezcan a uno solo de los tipos (por ejemplo, no podemos hacer .toUpperCase() si la variable también puede ser un number). Primero debemos "estrechar" el tipo.
Estrechamiento de Tipos (Type Narrowing)
El Type Narrowing es el proceso por el cual TypeScript analiza las estructuras de control de flujo (como if o switch) para deducir un tipo más específico para una variable en tiempo de ejecución.
Existen diferentes formas de realizar el narrowing:
1. Operador typeof
Ideal para distinguir tipos primitivos:
function printLength(value: string | number) {
if (typeof value === 'string') {
// Qui TypeScript sa che 'value' è una stringa
console.log(value.length);
} else {
// Qui TypeScript sa che 'value' è un numero
console.log(value.toFixed(2));
}
}2. Operador in
Se utiliza para verificar la presencia de una propiedad específica en un objeto:
interface Fish {
swim: () => void;
}
interface Bird {
fly: () => void;
}
function move(animal: Fish | Bird) {
if ('swim' in animal) {
animal.swim(); // Narrowing a Fish
} else {
animal.fly(); // Narrowing a Bird
}
}Uniones Discriminadas (Discriminated Unions)
El patrón de las Uniones Discriminadas consiste en crear objetos que comparten una propiedad común con un valor literal único (llamado discriminador). TypeScript reconoce este discriminador y realiza el narrowing automático dentro de los bloques condicionales.
interface SuccessResponse {
status: 'success'; // Discriminatore letterale
data: string;
}
interface ErrorResponse {
status: 'error'; // Discriminatore letterale
errorMessage: string;
}
type ApiResponse = SuccessResponse | ErrorResponse;
function handleResponse(response: ApiResponse) {
if (response.status === 'success') {
console.log('Dati ricevuti:', response.data);
} else {
console.error('Si è verificato un errore:', response.errorMessage);
}
}Pruébalo tú
Ejercicio 1: Unión de Tipos
Declara una variable llamada id que pueda ser tanto un número como una cadena. Inicialízala primero con el número 101, luego asigna el valor de cadena 'USER-101'.
Mostrar pista
Usa el operador | para unir number y string en la declaración de la variable let.
Solución disponible después de 3 intentos
Ejercicio 2: Type Narrowing Básico
Crea una función llamada formatInput que acepte un parámetro input de tipo cadena o número. Si input es una cadena, devuelve input convertido a mayúsculas. Si es un número, devuelve input multiplicado por 2. Especifica los tipos de forma explícita.
Mostrar pista
Usa typeof input === 'string' dentro de un blocco if para distinguir el comportamiento.
Solución disponible después de 3 intentos
Ejercicio 3: Narrowing con 'in'
Dadas las dos interfaces Car (con el método drive) y Boat (con el método sail), escribe una función llamada moveVehicle que acepte un parámetro vehicle de tipo Car o Boat. Si vehicle tiene la propiedad drive, ejecuta el método drive(). De lo contrario, ejecuta el método sail().
Mostrar pista
Usa el operador in de la forma 'drive' in vehicle para hacer el narrowing de la interfaz.
Solución disponible después de 3 intentos
Ejercicio 4: Unión Discriminada Shape
Define un tipo Shape que sea la unión de dos tipos: Circle y Square. Circle tiene una propiedad kind establecida en 'circle' (valor literal) y un radius (número). Square tiene una propiedad kind establecida en 'square' (valor literal) y un side (número). Luego escribe una función getArea que acepte shape de tipo Shape y devuelva el área como número (para el círculo Math.PI * radius * radius, para el cuadrado side * side).
Mostrar pista
Usa shape.kind === 'circle' dentro de getArea para discriminar el tipo y calcular el área correcta.
Solución disponible después de 3 intentos