Aprovechar los Tipos Utilitarios en TypeScript

TypeScript ofrece numerosos tipos utilitarios que te permiten manipular los tipos de manera dinámica y práctica. Ya sea para hacer propiedades opcionales, inmutables o para filtrar ciertos campos de un tipo, estos utilitarios pueden ayudarte a escribir código más limpio, más seguro y más legible.

En este artículo, descubrirás algunos de los tipos utilitarios más comúnmente utilizados en TypeScript, con ejemplos concretos para mostrarte cómo y cuándo usarlos.

1. Partial<T>

El tipo utilitario Partial<T> transforma todas las propiedades de un tipo T en propiedades opcionales. Esto es particularmente útil cuando trabajas con objetos donde no siempre se necesitan todas las propiedades.

Ejemplo

Imaginemos un tipo User con propiedades obligatorias:

typescript
1type User = {
2 id: number;
3 name: string;
4 email: string;
5};

Si quieres crear una función que actualice un usuario, pero que solo necesite un subconjunto de estas propiedades, puedes usar Partial<User>:

typescript
1function updateUser(user: User, updates: Partial<User>) {
2 return { ...user, ...updates };
3}
4
5// Uso
6const user: User = { id: 1, name: 'Alice', email: 'alice@example.com' };
7const updatedUser = updateUser(user, { name: 'Alicia' });

Gracias a Partial<User>, ahora puedes actualizar solo algunas propiedades sin tener que proporcionar todas las demás.

2. Readonly<T>

Readonly<T> hace que todas las propiedades de un tipo T sean inmutables. Esto significa que una vez que el objeto es creado, no puedes modificar sus propiedades, lo cual es ideal para objetos constantes.

Ejemplo

typescript
1type Config = {
2 apiEndpoint: string;
3 timeout: number;
4};
5
6const config: Readonly<Config> = {
7 apiEndpoint: 'https://api.example.com',
8 timeout: 5000,
9};
10
11// Error: No se puede asignar a 'timeout' porque es una propiedad de solo lectura
12config.timeout = 3000;

Al usar Readonly<Config>, te aseguras de que config permanezca inmutable durante toda la ejecución.

3. Pick<T, K>

El tipo utilitario Pick<T, K> crea un nuevo tipo seleccionando solo ciertas propiedades de un tipo T. Esto es útil cuando deseas crear un subtipo de un tipo existente.

Ejemplo

typescript
1type User = {
2 id: number;
3 name: string;
4 email: string;
5 address: string;
6};
7
8type UserSummary = Pick<User, 'id' | 'name'>;
9
10const userSummary: UserSummary = {
11 id: 1,
12 name: 'Alice',
13};
14
15// Error: La propiedad 'email' no existe en el tipo 'UserSummary'
16userSummary.email = 'alice@example.com';

Con Pick<User, "id" | "name">, creas un tipo UserSummary que contiene solo las propiedades id y name.

4. Omit<T, K>

Inversamente, Omit<T, K> crea un nuevo tipo excluyendo ciertas propiedades de un tipo T. Es lo opuesto a Pick.

Ejemplo

typescript
1type UserWithoutAddress = Omit<User, 'address'>;
2
3const userWithoutAddress: UserWithoutAddress = {
4 id: 1,
5 name: 'Alice',
6 email: 'alice@example.com',
7};
8
9// Error: La propiedad 'address' no existe en el tipo 'UserWithoutAddress'
10userWithoutAddress.address = '123 Main St';

En este ejemplo, UserWithoutAddress contiene todas las propiedades de User, excepto address.

5. Record<K, T>

Record<K, T> se utiliza para crear un tipo de objeto donde las claves K son de un tipo específico, y los valores son del tipo T. Esto es útil para crear objetos asociativos, como diccionarios o mapeos.

Ejemplo

Supongamos que quieres crear un objeto que asocie roles de usuario con permisos de acceso:

typescript
1type Role = 'admin' | 'user' | 'guest';
2type Permissions = 'read' | 'write' | 'delete';
3
4const rolePermissions: Record<Role, Permissions[]> = {
5 admin: ['read', 'write', 'delete'],
6 user: ['read', 'write'],
7 guest: ['read'],
8};

En este ejemplo, Record<Role, Permissions[]> asegura que el objeto rolePermissions contenga todas las claves admin, user, y guest, con valores de tipo Permissions[].

6. Exclude<T, U>

Exclude<T, U> permite crear un tipo excluyendo ciertos tipos de otro tipo. Es útil para filtrar tipos específicos de un Union Type.

Ejemplo

typescript
1type Status = 'active' | 'inactive' | 'suspended';
2
3type ActiveStatus = Exclude<Status, 'suspended'>;
4
5const status: ActiveStatus = 'active'; // Válido
6const anotherStatus: ActiveStatus = 'suspended'; // Error

Usando Exclude<Status, "suspended">, creas un tipo ActiveStatus que no puede ser "suspended".

Conclusión

Los tipos utilitarios de TypeScript te permiten expresar tipos complejos de manera concisa y legible. Al aprovechar Partial, Readonly, Pick, Omit, Record, y Exclude, puedes manipular tus tipos de forma dinámica y responder a las necesidades de tu aplicación sin sobrecargar tu código.

Estos utilitarios son solo la punta del iceberg: TypeScript ofrece muchos otros tipos utilitarios avanzados. Tómate el tiempo de explorar estas herramientas, y tu código será más mantenible, más seguro y más fácil de leer.


Aplicando estos consejos, podrás manipular tus tipos de manera más eficiente y escribir código TypeScript aún más robusto. ¡Buen código!

Comparte este artículo


Sébastien Timoner

Sébastien TIMONER

Desarrollador Líder
Experto en Desarrollo a Medida
Aix-en-Provence, France

Experto en desarrollo web y gestión de equipos técnicos, me especializo en la creación y optimización de soluciones digitales de alto rendimiento. Gracias a un profundo dominio de tecnologías modernas como React.js, Node.js, TypeScript y Symfony, garantizo el éxito de proyectos SaaS complejos, desde el diseño hasta la implementación, para empresas de diversos sectores, dentro de offroadLabs.

En offroadLabs, ofrezco servicios de desarrollo a medida, combinando experiencia técnica y enfoque colaborativo. Ya sea para crear una solución SaaS innovadora, modernizar una aplicación existente o acompañar el desarrollo de habilidades de un equipo, me comprometo a proporcionar soluciones robustas y eficientes, adaptadas a las necesidades específicas de cada proyecto.

Estoy disponible para proyectos en la zona de Aix-en-Provence o en modalidad totalmente remota.