TypeScript es mucho más que añadir tipos a JavaScript. Su sistema de tipos es tan potente que funciona como un lenguaje de programación en sí mismo. Dominar sus características avanzadas te permite escribir código más seguro, expresivo y autodocumentado.
Generics: Código reutilizable con tipos
Los genéricos permiten crear funciones, clases e interfaces que funcionan con cualquier tipo manteniendo la seguridad de tipos.
// ❌ Sin genéricos: perdemos información de tipo
function getFirst(arr: any[]): any {
return arr[0];
}
// ✅ Con genéricos: el tipo se preserva
function getFirst<T>(arr: T[]): T {
return arr[0];
}
const num = getFirst([1, 2, 3]); // tipo: number 🎉
const str = getFirst(['a', 'b']); // tipo: string 🎉
Restricciones con extends
interface HasId {
id: string | number;
}
function findById<T extends HasId>(items: T[], id: T['id']): T | undefined {
return items.find(item => item.id === id);
}
Utility Types: Los tipos incluidos
Partial<T> — Todas las propiedades opcionales
interface User {
name: string;
email: string;
age: number;
}
function updateUser(id: string, changes: Partial<User>): void {
// changes puede tener cualquier combinación de name, email, age
}
Pick<T, K> y Omit<T, K> — Seleccionar/excluir propiedades
type PublicUser = Omit<User, 'password'>;
type UserListItem = Pick<User, 'id' | 'name'>;
type CreateUserDto = Omit<User, 'id' | 'createdAt'>;
Record<K, V> — Diccionarios tipados
type UserRole = 'admin' | 'editor' | 'viewer';
const permissions: Record<UserRole, RolePermissions> = {
admin: { canEdit: true, canDelete: true, canPublish: true },
editor: { canEdit: true, canDelete: false, canPublish: true },
viewer: { canEdit: false, canDelete: false, canPublish: false },
};
Type Guards: Estrechamiento de tipos
interface Dog { kind: 'dog'; bark(): void; }
interface Cat { kind: 'cat'; meow(): void; }
type Animal = Dog | Cat;
function isDog(animal: Animal): animal is Dog {
return animal.kind === 'dog';
}
function handleAnimal(animal: Animal) {
if (isDog(animal)) {
animal.bark(); // TypeScript sabe que es Dog aquí
} else {
animal.meow(); // TypeScript sabe que es Cat aquí
}
}
Discriminated Unions
type ApiResponse<T> =
| { status: 'loading' }
| { status: 'success'; data: T }
| { status: 'error'; error: string };
function handleResponse<T>(response: ApiResponse<T>) {
switch (response.status) {
case 'loading': return 'Cargando...';
case 'success': return response.data;
case 'error': return response.error;
}
}
Template Literal Types
type EventName = 'click' | 'focus' | 'blur';
type Handler = `on${Capitalize<EventName>}`;
// tipo: 'onClick' | 'onFocus' | 'onBlur'
Conclusión
TypeScript avanzado es una inversión que se paga sola. Cada tipo bien definido es un bug que nunca llegará a producción. Empieza añadiendo genéricos a tus funciones de utilidad, usa Utility Types para no repetir interfaces, y adopta discriminated unions para manejar estados de forma segura.