TypeScript se ha convertido en el estándar de la industria para el desarrollo web a gran escala. Si bien la mayoría de los desarrolladores conocen los conceptos básicos de interfaces y tipos, el verdadero poder reside en su sistema de tipos avanzado. Aquí hay 5 patrones que te distinguirán como ingeniero senior.
1. Restricciones Genéricas
Los genéricos son poderosos, pero a veces necesitas limitar lo que se puede pasar. extends es tu amigo aquí.
interface HasId { id: string; } function getById<T extends HasId>(list: T[], id: string): T | undefined { return list.find((item) => item.id === id); }
Al asegurar que T extiende HasId, garantizamos que acceder a .id dentro de la función es seguro.
2. Tipos Condicionales
Los tipos condicionales te permiten crear mapeos de tipos no uniformes. La sintaxis es similar al operador ternario en JavaScript.
type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false
Un caso de uso práctico es filtrar tipos de una unión:
type Diff<T, U> = T extends U ? never : T; type NonNullable<T> = Diff<T, null | undefined>;
3. Tipos Mapeados
Los tipos mapeados te permiten crear nuevos tipos basados en los antiguos transformando propiedades.
type ReadOnly<T> = { readonly [P in keyof T]: T[P]; }; interface User { name: string; age: number; } type ReadOnlyUser = ReadOnly<User>;
Incluso puedes agregar o eliminar modificadores:
type Mutable<T> = { -readonly [P in keyof T]: T[P]; };
4. Tipos Literales de Plantilla
Introducidos en TypeScript 4.1, estos te permiten manipular tipos de cadenas directamente.
type World = 'world'; type Greeting = `hello ${World}`; // "hello world" type Color = 'red' | 'blue'; type Quantity = 'light' | 'dark'; type Palette = `${Quantity}-${Color}`; // "light-red" | "light-blue" | "dark-red" | "dark-blue"
Esto es increíblemente útil para tipificar cadenas que siguen un patrón específico, como clases CSS o nombres de eventos.
5. La Palabra Clave infer
La palabra clave infer dentro de los tipos condicionales te permite extraer tipos de otros tipos.
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; function check(): boolean { return true; } type CheckReturn = ReturnType<typeof check>; // boolean
Aquí, le pedimos a TypeScript que "infiera" el tipo de retorno R de una función y lo devuelva.
Conclusión
Dominar estos patrones te permite escribir bibliotecas y utilidades que son robustas y brindan una excelente experiencia de desarrollador (DX). El objetivo de TypeScript avanzado no es la complejidad por la complejidad, sino la seguridad y la expresividad.