
TypeScript Avanzado: Tipos, Genéricos y Decoradores
Domina TypeScript avanzado. Aprende tipos complejos, genéricos, decoradores y patrones de diseño en TypeScript.
TypeScript Avanzado: Tipos, Genéricos y Decoradores
TypeScript va mucho más allá del tipado básico. En este artículo exploraremos características avanzadas que transformarán la forma en que escribes código: tipos complejos, genéricos potentes, decoradores, tipos mapeados y utilidades de tipo integradas. Dominar estas características te convertirá en un desarrollador TypeScript de clase mundial.
¿Por qué TypeScript Avanzado importa?
TypeScript es mucho más que un simple verificador de tipos. Con sus características avanzadas, puedes crear APIs type-safe increíblemente expresivas, prevenir clases enteras de bugs, y hacer que tu código sea auto-documentado.
Dato: Los desarrolladores que dominan TypeScript avanzado son capaces de escribir código con 40% menos bugs según estudios de la industria.
Tipos Complejos - Más allá de number, string, boolean
Union Types - Múltiples posibilidades
Un union type permite que una variable sea uno de varios tipos posibles.
type ID = string | number;
type Status = 'pending' | 'approved' | 'rejected';
type Response = { success: true; data: any } | { success: false; error: string };
const userId: ID = 123; // ✅ Válido
const id2: ID = 'ABC-123'; // ✅ Válido
const id3: ID = true; // ❌ Error
Discriminated Unions - Tipos seguros y elegantes
Combine union types con un propiedad discriminadora para type-safe handling.
type Success = { status: 'success'; data: string };
type Error = { status: 'error'; message: string };
type Result = Success | Error;
function handleResult(result: Result) {
if (result.status === 'success') {
console.log(result.data); // TypeScript sabe que data existe
} else {
console.log(result.message); // TypeScript sabe que message existe
}
}
Intersection Types - Combinando tipos
Intersection types permiten combinar múltiples tipos en uno.
interface Admin {
isAdmin: boolean;
permissions: string[];
}
interface User {
id: number;
name: string;
email: string;
}
type AdminUser = Admin & User;
const adminUser: AdminUser = {
isAdmin: true,
permissions: ['read', 'write'],
id: 1,
name: 'Juan',
email: 'juan@example.com'
};
Conditional Types - Lógica en el sistema de tipos
Los conditional types permiten elegir un tipo basado en otro tipo (similares a ternarios).
type IsString = T extends string ? true : false;
type A = IsString<'hello'>; // tipo true
type B = IsString<123>; // tipo false
// Ejemplo más práctico
type Flatten = T extends Array ? U : T;
type Str = Flatten; // string
type Num = Flatten; // number
Mapped Types - Transformando tipos dinámicamente
Los mapped types permiten crear nuevos tipos basados en propiedades de tipos existentes.
Creando tipos readonly
type User = {
id: number;
name: string;
email: string;
};
type ReadonlyUser = {
readonly [K in keyof User]: User[K];
};
// Equivalente a:
// type ReadonlyUser = {
// readonly id: number;
// readonly name: string;
// readonly email: string;
// };
Tipos getters/setters
type Getters = {
[K in keyof T as `get${Capitalize}`]: () => T[K];
};
interface User {
name: string;
age: number;
}
type UserGetters = Getters;
// type UserGetters = {
// getName: () => string;
// getAge: () => number;
// };
Genéricos - La Herramienta más Poderosa
Los genéricos permiten escribir componentes, funciones e interfaces reutilizables que trabajen con cualquier tipo manteniendo type-safety.
Genéricos básicos
// Función genérica
function identity(arg: T): T {
return arg;
}
const result1 = identity('hola'); // string
const result2 = identity(42); // number
const result3 = identity('inferido'); // TypeScript infiere string
Constraints en genéricos
A veces necesitas restringir qué tipos pueden ser pasados a un genérico.
// Solo tipos con propiedad 'length'
function getLength(arg: T): number {
return arg.length;
}
getLength('hello'); // ✅ Strings tienen length
getLength([1, 2, 3]); // ✅ Arrays tienen length
getLength(123); // ❌ Numbers no tienen length
// Extender otro genérico
function getProperty(obj: T, key: K) {
return obj[key];
}
const user = { id: 1, name: 'Juan' };
getProperty(user, 'name'); // ✅ 'name' es una propiedad válida
getProperty(user, 'email'); // ❌ 'email' no existe en user
Genéricos en clases
class Stack {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
}
const numberStack = new Stack();
numberStack.push(42);
const value = numberStack.pop(); // number | undefined
Decoradores - Metaprogramación en TypeScript
Los decoradores permiten anotar y modificar clases, propiedades, métodos y parámetros. Son especialmente útiles en frameworks como NestJS.
Decoradores de clase
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class User {
name: string = 'Juan';
}
const user = new User();
user.newProperty = 'value'; // ❌ Error: no puedes agregar propiedades
Decoradores de método
function deprecated(target: any, property: string, descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function(...args: any[]) {
console.warn(`⚠️ ${property} está deprecado`);
return originalMethod.apply(this, args);
};
return descriptor;
}
class API {
@deprecated
oldMethod() {
return 'datos';
}
}
Decoradores de parámetro
function validate(target: any, propertyKey: string, parameterIndex: number) {
// Metadata que indica qué parámetro validar
}
class UserService {
createUser(@validate name: string, email: string) {
// Validar nombre
}
}
Utility Types - Herramientas incorporadas de TypeScript
TypeScript incluye muchos tipos de utilidad para manipular tipos de manera común.
Partial - Hace todas las propiedades opcionales
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = Partial;
// Equivalente a:
// {
// id?: number;
// name?: string;
// email?: string;
// }
Required - Hace todas las propiedades requeridas
interface Config {
apiKey?: string;
timeout?: number;
}
type RequiredConfig = Required;
// Las propiedades ahora son requeridas
Readonly - Hace todas las propiedades readonly
interface User {
id: number;
name: string;
}
type ReadonlyUser = Readonly;
const user: ReadonlyUser = { id: 1, name: 'Juan' };
user.name = 'Pedro'; // ❌ Error: no puedes modificar
Record - Crear tipos con llaves específicas
type Role = 'admin' | 'user' | 'guest';
type Permissions = Record;
const permissions: Permissions = {
admin: ['read', 'write', 'delete'],
user: ['read', 'write'],
guest: ['read']
};
Pick y Omit
interface User {
id: number;
name: string;
email: string;
password: string;
}
// Pick: selecciona propiedades específicas
type UserPreview = Pick;
// { id: number; name: string; }
// Omit: excluye propiedades específicas
type UserPublic = Omit;
// { id: number; name: string; email: string; }
Exclude e Include
type Status = 'pending' | 'approved' | 'rejected' | 'archived';
type ActiveStatus = Exclude;
// 'pending' | 'approved' | 'rejected'
type APIResponse = { data: T } | { error: string };
type SuccessResponse = Extract<
APIResponse<{ id: number }>,
{ data: any }
>;
// { data: { id: number } }
Patterns avanzados reales
Type-safe Event Emitter
type EventMap = {
'user:created': { id: number; name: string };
'user:deleted': { id: number };
'error': { message: string };
};
class EventEmitter> {
on(
event: K,
callback: (data: Events[K]) => void
): void {
// implementación
}
emit(
event: K,
data: Events[K]
): void {
// implementación
}
}
const emitter = new EventEmitter();
emitter.on('user:created', (data) => {
console.log(data.id, data.name); // type-safe!
});
emitter.emit('user:created', { id: 1, name: 'Juan' }); // ✅
emitter.emit('user:created', { id: 1 }); // ❌ Error: falta name
Conclusión: TypeScript Avanzado = Código Mejor
Las características avanzadas de TypeScript pueden parecer intimidantes al principio, pero son increíblemente poderosas cuando las dominas. Type-safety, mejor autocompletado del IDE, y documentación auto-generada hacen que valga la pena el esfuerzo de aprender.
Comienza a experimentar con estos conceptos en tus proyectos y verás cómo tu código se vuelve más robusto, mantenible y a prueba de bugs.

Sobre el autor
Andres Saumet
Desarrollador Web & Móvil Full Stack · Colombia
Hay mil desarrolladores que pueden hacer que algo funcione. Yo me obsesiono con hacer que funcione y genere dinero.
Soy Andres Saumet, desarrollador Web y Móvil con foco en rentabilidad. Trabajo con startups, emprendedores y empresas que ya tienen una visión clara y necesitan a alguien que la convierta en un producto digital real — uno que los usuarios quieran usar y que el negocio quiera escalar.
Domino React, Next.js, React Native y Node.js. Pero más allá del stack, entiendo cómo piensan los usuarios, cómo fluye un negocio y qué decisiones técnicas impactan directamente en los ingresos.
Cada línea de código que escribo tiene un propósito: que tu producto web o móvil sea más rápido, más usable y más rentable.
¿Tienes un producto que necesita crecer? Construyámoslo juntos.
