El conocimiento es el nuevo dinero.
Aprender es la nueva manera en la que inviertes
Acceso Cursos

Explorando Tipos Mapeados en TypeScript: 8 Ejemplos de Básico a Avanzado

· 5 min de lectura
Explorando Tipos Mapeados en TypeScript: 8 Ejemplos de Básico a Avanzado

Los tipos mapeados en TypeScript son potentes herramientas para transformar las propiedades de un tipo en otro. Son similares a los métodos de array como map y filter, pero estas operaciones se realizan sobre tipos.

Entenderemos su uso a través de ejemplos prácticos. A continuación, mostraremos progresivamente 8 ejemplos de tipos mapeados, desde los más básicos hasta los más avanzados, que te permitirán dominar fácilmente esta potente herramienta de transformación de tipos.

Transformación básica de tipos


En TypeScript, a veces necesitamos transformar las propiedades de un tipo en otro. Esto puede lograrse fácilmente utilizando tipos mapeados.

A continuación, demostraremos cómo transformar las propiedades de un tipo Product en tipos string a través de un ejemplo concreto.

CPU
1 vCPU
MEMORIA
1 GB
ALMACENAMIENTO
10 GB
TRANSFERENCIA
1 TB
PRECIO
$ 4 mes
Para obtener el servidor GRATIS debes de escribir el cupon "LEIFER"

Definir el tipo Producto


En primer lugar, definimos un tipo Producto, que incluye tres propiedades: nombre (tipo cadena), precio (tipo número), e inStock (tipo booleano).

type Product = {
    name: string;
    price: number;
    inStock: boolean;
};

Definir el tipo ProductToString


A continuación, definimos un nuevo tipo ProductToString, que transforma todas las propiedades del tipo Product en tipos string.

type ProductToString = {
    [Key in keyof Product]: string;
};

Tipo resultante
Finalmente, el tipo ProductToString resultante es el siguiente:

type ProductToString = {
    name: string;
    price: string;
    inStock: string;
};

Hacer opcionales las propiedades de un tipo


En TypeScript, a menudo necesitamos hacer que todas las propiedades de un tipo sean opcionales. Normalmente, usamos el tipo de utilidad Partial incorporado para conseguirlo, pero también podemos conseguir el mismo efecto usando tipos mapeados.

Definir tipo de producto

type Product = {
    name: string;
    price: number;
    inStock: boolean;
};

Utilizar tipos asignados para que las propiedades sean opcionales

type ProductToOptional = {
    [Key in keyof Product]?: Product[Key];
};

Tipo resultante

type ProductToOptional = {
    name?: string;
    price?: number;
    inStock?: boolean;
};

Convertir propiedades opcionales en obligatorias


En TypeScript, a veces necesitamos convertir todas las propiedades opcionales de un tipo en propiedades requeridas. Esto se puede lograr fácilmente utilizando tipos mapeados.

Definir tipo de producto

type Product = {
    name?: string;
    price?: number;
    inStock?: boolean;
};

Definir el tipo ProductToRequired

type ProductToRequired = {
    [Key in keyof Product]-?: Product[Key];
};

Tipo resultante

type ProductToRequired = {
    name: string;
    price: number;
    inStock: boolean;
};

Hacer que las propiedades sean de sólo lectura


En TypeScript, a veces necesitamos que todas las propiedades de un tipo sean de sólo lectura. Esto se puede lograr fácilmente utilizando tipos mapeados.

Definir tipo de producto

type Product = {
    name: string;
    price: number;
    inStock: boolean;
};

Definir el tipo ProductToReadonly

type ProductToReadonly = {
    readonly [Key in keyof Product]: Product[Key];
};

Tipo resultante

type ProductToReadonly = {
    readonly name: string;
    readonly price: number;
    readonly inStock: boolean;
};

Eliminación de ciertas propiedades


En TypeScript, a veces necesitamos eliminar ciertas propiedades de un tipo. Por lo general, utilizamos el tipo de utilidad Omit incorporado para lograr esto, pero también podemos lograr el mismo efecto utilizando tipos mapeados.

Definir tipo de producto

type Product = {
    name: string;
    price: number;
    inStock: boolean;
};

Utilizar tipos asignados para eliminar propiedades

type ProductWithoutPrice = {
    [Key in keyof Product as Key extends 'price' ? never : Key]: Product[Key];
};

Tipo resultante

type ProductWithoutPrice = {
    name: string;
    inStock: boolean;
};

Creación de un tipo sólo con propiedades de un tipo específico


En TypeScript, podemos utilizar tipos condicionales para crear un nuevo tipo que sólo incluya propiedades de un determinado tipo.

Definir tipo de producto

type Product = {
    name: string;
    price: number;
    inStock: boolean;
    tags: string[];
};

Definir el tipo OnlyStringProperties

type OnlyStringProperties<Type> = {
    [Key in keyof Type as Type[Key] extends string ? Key : never]: Type[Key];
};

Utilizar OnlyStringProperties

type ProductOnlyStringProperties = OnlyStringProperties<Product>;

Tipo resultante

type ProductOnlyStringProperties = {
    name: string;
};

Creación de nuevos nombres de propiedades usando tipos literales de plantilla


En TypeScript, podemos utilizar tipos literales de plantilla para crear un nuevo tipo con nombres de propiedades que tengan un prefijo específico y mayúsculas.

Definir Tipo Produce

type Product = {
    name: string;
    price: number;
    inStock: boolean;
};

Creación de un tipo con propiedades prefijadas por get


En TypeScript, podemos usar tipos literales de plantilla para crear un nuevo tipo con nombres de propiedades prefijados por get.

type Getters<Type> = {
    [Key in keyof Type as `get${Capitalize<string & Key>}`]: () => Type[Key];
};

Utilizar captadores

type ProductGetters = Getters<Product>;

Tipo resultante

type ProductGetters = {
    getName: () => string;
    getPrice: () => number;
    getInStock: () => boolean;
};

Tipos mapeados anidados basados en condiciones


En TypeScript, podemos crear una lógica de transformación de tipos más compleja combinando tipos mapeados y tipos condicionales. Por ejemplo, podemos generar diferentes estructuras de tipos anidados basadas en los tipos de propiedades.

Definir tipo NestedObject


Primero, definimos un tipo NestedObject, que incluye varios tipos de propiedades, incluyendo objetos anidados.

type NestedObject = {
    id: number;
    name: string;
    metadata: {
        createdAt: Date;
        updatedAt: Date;
    };
    tags: string[];
};

Definir el tipo DeepReadonly


A continuación, definimos un tipo DeepReadonly que convertirá todas las propiedades de un objeto, incluidas las propiedades de los objetos anidados, en sólo lectura.

type DeepReadonly<T> = {
    readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

En esta definición, T[P] extends object se utiliza para comprobar si el tipo de propiedad es un objeto. Si es un objeto, DeepReadonly se aplica recursivamente; en caso contrario, la propiedad se establece como de sólo lectura.

Utilizar el tipo DeepReadonly


Podemos utilizar DeepReadonly para definir un nuevo tipo ReadonlyNestedObject, que es una versión de sólo lectura profunda de NestedObject.

type ReadonlyNestedObject = DeepReadonly<NestedObject>;

Tipo resultante


Finalmente, el tipo ReadonlyNestedObject resultante es el siguiente:

type ReadonlyNestedObject = {
    readonly id: number;
    readonly name: string;
    readonly metadata: {
        readonly createdAt: Date;
        readonly updatedAt: Date;
    };
    readonly tags: readonly string[];
};

Ejemplo de uso

const readonlyNestedObject: ReadonlyNestedObject = {
    id: 1,
    name: "Example",
    metadata: {
        createdAt: new Date(),
        updatedAt: new Date()
    },
    tags: ["typescript", "programming"]
};

// readonlyNestedObject.id = 2; // Error: Cannot assign to 'id' because it is a read-only property.
// readonlyNestedObject.metadata.createdAt = new Date(); // Error: Cannot assign to 'createdAt' because it is a read-only property.

Los tipos mapeados en TypeScript son una característica increíblemente poderosa que nos permite lograr varias transformaciones de tipo complejas. Se pueden utilizar para:

  • Transformar propiedades: Cambiar los tipos de las propiedades existentes en un tipo.
  • Añadir o eliminar propiedades: Añadir nuevas propiedades o eliminar las existentes.
  • Controlar la opcionalidad y el estado de sólo lectura: Haga que las propiedades sean opcionales o de sólo lectura.
  • Crear tipos dinámicos: Construya nuevos tipos utilizando tipos condicionales y tipos literales de plantilla, adecuados para escenarios avanzados (por ejemplo, generando getters y setters).
  • Mientras que los tipos de utilidad incorporados como Partial, Readonly y Omit proporcionan atajos convenientes, los tipos mapeados nos dan una comprensión más profunda y un control preciso sobre los tipos.

Espero que este artículo te ayude a dominar mejor estas técnicas, haciendo tu código más limpio, más predecible y más fácil de mantener.

Fuente