Tipos de utilidad en TypeScript

Tipos de utilidad en TypeScript

Este artículo explica los tipos de utilidad en TypeScript.

YouTube Video

Tipos de utilidad en TypeScript

Los tipos de utilidad en TypeScript son herramientas convenientes para crear nuevos tipos basados en tipos existentes. Esto permite definiciones de tipos más flexibles y aumenta la reutilización del código. Aquí, explicaremos en detalle los tipos de utilidad más comúnmente utilizados y discutiremos cómo usar cada uno con ejemplos de código.

Partial<T>

Partial<T> hace que todas las propiedades de un tipo de objeto sean opcionales (permitiendo undefined). Es útil cuando quieres usar solo algunas de las propiedades que tiene el tipo original.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5}
 6
 7function updateUser(user: Partial<User>) {
 8  console.log(user);
 9}
10
11updateUser({ name: "Alice" }); // Updates only 'name'

Explicación:

En el ejemplo anterior, usar Partial<User> hace que todas las propiedades del tipo User sean opcionales. Por lo tanto, en la función updateUser, puedes pasar solo un subconjunto de las propiedades.

Required<T>

Required<T> hace que todas las propiedades, incluidas las opcionales, sean obligatorias. Se usa cuando quieres convertir propiedades opcionales en obligatorias.

 1interface User {
 2  id: number;
 3  name?: string;
 4  age?: number;
 5}
 6
 7function createUser(user: Required<User>) {
 8  console.log(user);
 9}
10
11// createUser({ id: 1 }); // Error: 'name' and 'age' are required
12createUser({ id: 1, name: "Alice", age: 25 });

Explicación:

Al usar Required<User>, propiedades como name y age se tratan como obligatorias.

Readonly<T>

Readonly<T> hace que todas las propiedades de un objeto sean de solo lectura. Esto evita que los valores del objeto sean modificados.

 1interface User {
 2  id: number;
 3  name: string;
 4}
 5
 6const user: Readonly<User> = {
 7  id: 1,
 8  name: "Alice"
 9};
10
11// user.id = 2; // Error: 'id' is read-only
12console.log(user);

Explicación:

Al usar Readonly<T>, puedes proteger las propiedades de un objeto para que no sean alteradas. Es efectivo cuando quieres evitar que los datos sean modificados accidentalmente durante el desarrollo.

Record<K, T>

Record<K, T> crea un tipo de mapa con tipos de clave y valor específicos. K es el tipo para las claves (como string o number), y T es el tipo para los valores.

 1type Roles = "admin" | "user" | "guest";
 2interface Permissions {
 3  read: boolean;
 4  write: boolean;
 5}
 6
 7const rolePermissions: Record<Roles, Permissions> = {
 8  admin: { read: true, write: true },
 9  user: { read: true, write: false },
10  guest: { read: false, write: false },
11};
12
13console.log(rolePermissions);

Explicación:

Record<K, T> es útil cuando quieres definir pares clave-valor. En el ejemplo anterior, los permisos se definen según los roles de usuario.

Pick<T, K>

Pick<T, K> extrae solo las propiedades especificadas de un tipo de objeto. Puedes crear un nuevo tipo extrayendo solo las propiedades necesarias.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5}
 6
 7type UserSummary = Pick<User, "id" | "name">;
 8
 9const summary: UserSummary = {
10  id: 1,
11  name: "Alice"
12};
13
14console.log(summary);

Explicación:

Al usar Pick<T, K>, puedes extraer propiedades específicas de un tipo de objeto y tratarlas como un nuevo tipo. Por ejemplo, solo id y name se extraen en el ejemplo anterior.

Omit<T, K>

Omit<T, K> excluye propiedades específicas de un tipo de objeto. Esta es la operación opuesta a Pick.

 1interface User {
 2  id: number;
 3  name: string;
 4  age: number;
 5  email: string;
 6}
 7
 8type UserWithoutEmail = Omit<User, "email">;
 9
10const userWithoutEmail: UserWithoutEmail = {
11  id: 1,
12  name: "Alice",
13  age: 25
14};
15
16console.log(userWithoutEmail);

Explicación:

Al usar Omit<T, K>, puedes crear un nuevo tipo excluyendo propiedades específicas. En el ejemplo anterior, la propiedad email está excluida.

Exclude<T, U>

Exclude<T, U> crea un nuevo tipo eliminando el tipo U del tipo de unión T. Se utiliza cuando quieres eliminar un tipo específico.

1type Status = "active" | "inactive" | "pending";
2type ExcludedStatus = Exclude<Status, "pending">;
3
4const status: ExcludedStatus = "active"; // "pending" is excluded, so it cannot be chosen
5console.log(status);

Explicación:

Al usar Exclude<T, U>, puedes eliminar tipos innecesarios dentro de un tipo de unión. En el ejemplo anterior, dado que "pending" está excluido, solo se pueden seleccionar "active" o "inactive".

Extract<T, U>

Extract<T, U> extrae las partes que coinciden con el tipo U dentro del tipo de unión T. Es útil cuando deseas extraer solo un tipo específico.

1type Status = "active" | "inactive" | "pending";
2type ActiveStatus = Extract<Status, "active" | "pending">;
3
4const status: ActiveStatus = "active"; // "inactive" cannot be chosen
5console.log(status);

Explicación:

Extract<T, U> realiza la operación opuesta a Exclude. En el ejemplo anterior, solo se pueden seleccionar "active" y "pending".

NonNullable<T>

NonNullable<T> crea un tipo excluyendo null y undefined. Es útil cuando deseas excluir estos valores de tipos opcionales.

1type UserName = string | null | undefined;
2type ValidUserName = NonNullable<UserName>;
3
4const name: ValidUserName = "Alice"; // null and undefined cannot be chosen
5console.log(name);

Explicación:

NonNullable<T> excluye null y undefined de un tipo que los contiene. Esto es útil cuando deseas asegurarte de que un valor exista definitivamente.

Conclusión

Los tipos utilitarios de TypeScript son herramientas poderosas para hacer que las definiciones de tipos sean más concisas y flexibles. Entender y usar adecuadamente los tipos utilitarios básicos como Partial, Required, Readonly, Record, etc., puede mejorar la reutilización y el mantenimiento del código. Dominar estos tipos permite definiciones de tipos más robustas y seguras, lo que facilita un desarrollo eficiente.

El operador keyof de TypeScript

El operador keyof de TypeScript se utiliza para obtener todos los nombres de propiedades de un tipo de objeto. Usando este operador, se pueden obtener las claves de un tipo de objeto como un tipo de unión. Esto es extremadamente útil para escribir código protegido por tipos.

Uso básico

1interface Person {
2    name: string;
3    age: number;
4    email: string;
5}
6
7// Use keyof to get the property names of Person
8type PersonKeys = keyof Person; // "name" | "age" | "email"

Uso de Ejemplo

  1. Usándolo en argumentos de funciones

    Se puede usar keyof para definir una función que tenga tipos basados en propiedades específicas de un objeto.

1function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
2    return obj[key];
3}
4
5const person: Person = { name: "Alice", age: 30, email: "alice@example.com" };
6const name = getProperty(person, "name"); // type is string
7const age = getProperty(person, "age");   // type is number
  1. Mejorando las restricciones de tipos

    Al usar keyof, puedes garantizar que las claves pasadas a una función se validen en tiempo de compilación.

1// Passing an invalid property name results in an error
2const invalid = getProperty(person, "invalidKey"); // Error

Resumen

  • El operador keyof se utiliza para obtener todos los nombres de propiedades de un tipo de objeto.
  • Puedes obtener los nombres de propiedades como un tipo de unión, logrando un código seguro para los tipos.
  • Usándolo en argumentos de función, puedes restringir la función para que acepte solo nombres de propiedades válidos.

De esta forma, el operador keyof mejora la seguridad de tipos en TypeScript y te ayuda a escribir un código más robusto.

Puedes seguir el artículo anterior utilizando Visual Studio Code en nuestro canal de YouTube. Por favor, también revisa nuestro canal de YouTube.

YouTube Video