TypeScript: Utility Types Avanzados que Debes Conocer

9 de diciembre de 2025
Osman Jimenez
TypeScript Desarrollo Web Programación

Dominando los Utility Types de TypeScript

TypeScript incluye tipos de utilidad poderosos que pueden transformar y manipular tipos existentes. Estos son esenciales para escribir código type-safe y mantenible.

Utility Types Básicos

Partial y Required

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// Partial: Hace todas las propiedades opcionales
type PartialUser = Partial;
// { id?: number; name?: string; email?: string; age?: number; }

// Required: Hace todas las propiedades requeridas
type RequiredUser = Required;
// { id: number; name: string; email: string; age: number; }

Pick y Omit

// Pick: Selecciona propiedades específicas
type UserPreview = Pick;
// { id: number; name: string; }

// Omit: Excluye propiedades específicas
type UserWithoutAge = Omit;
// { id: number; name: string; email: string; }

Utility Types Avanzados

Record

// Record: Crea un objeto con claves y valores específicos
type UserRoles = 'admin' | 'user' | 'guest';
type RolePermissions = Record;

const permissions: RolePermissions = {
  admin: ['read', 'write', 'delete'],
  user: ['read', 'write'],
  guest: ['read']
};

ReturnType y Parameters

function createUser(name: string, age: number) {
  return { name, age, createdAt: new Date() };
}

// ReturnType: Extrae el tipo de retorno
type User = ReturnType;
// { name: string; age: number; createdAt: Date; }

// Parameters: Extrae los tipos de parámetros
type CreateUserParams = Parameters;
// [name: string, age: number]

Awaited

type AsyncUser = Promise<{ id: number; name: string }>;

// Awaited: Desenvuelve el tipo de una Promise
type User = Awaited;
// { id: number; name: string; }

Utility Types Personalizados

DeepPartial

type DeepPartial = {
  [P in keyof T]?: T[P] extends object
    ? DeepPartial
    : T[P];
};

interface Config {
  database: {
    host: string;
    port: number;
    credentials: {
      user: string;
      password: string;
    };
  };
}

type PartialConfig = DeepPartial;
// Todas las propiedades anidadas son opcionales

Nullable y NonNullable

type Nullable = T | null | undefined;
type NonNullable = T extends null | undefined ? never : T;

type MaybeString = Nullable;
// string | null | undefined

type DefiniteString = NonNullable;
// string

ReadonlyDeep

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

interface MutableConfig {
  settings: {
    theme: string;
    language: string;
  };
}

type ImmutableConfig = ReadonlyDeep;
// Todas las propiedades son readonly recursivamente

Conditional Types

// Extrae tipos de arrays
type ArrayElement = T extends (infer E)[] ? E : never;

type Numbers = ArrayElement; // number
type Strings = ArrayElement; // string

// Filtra tipos
type FilterByType = {
  [K in keyof T as T[K] extends U ? K : never]: T[K];
};

interface Mixed {
  id: number;
  name: string;
  age: number;
  active: boolean;
}

type OnlyNumbers = FilterByType;
// { id: number; age: number; }

Template Literal Types

type EventName = 'click' | 'focus' | 'blur';
type EventHandler = `on${Capitalize}`;
// 'onClick' | 'onFocus' | 'onBlur'

type HTTPMethod = 'GET' | 'POST' | 'PUT' | 'DELETE';
type Endpoint = `/api/${Lowercase}`;
// '/api/get' | '/api/post' | '/api/put' | '/api/delete'

Casos de Uso Prácticos

Form State Management

type FormState = {
  values: T;
  errors: Partial>;
  touched: Partial>;
  isValid: boolean;
};

interface LoginForm {
  email: string;
  password: string;
}

const formState: FormState = {
  values: { email: '', password: '' },
  errors: {},
  touched: {},
  isValid: false
};

API Response Types

type ApiResponse = 
  | { success: true; data: T }
  | { success: false; error: string };

type UserResponse = ApiResponse;

function handleResponse(response: UserResponse) {
  if (response.success) {
    console.log(response.data); // Type: User
  } else {
    console.error(response.error); // Type: string
  }
}

Mejores Prácticas

  1. Usa utility types nativos: Antes de crear uno personalizado
  2. Mantén tipos simples: La complejidad excesiva dificulta el mantenimiento
  3. Documenta tipos complejos: Usa comentarios JSDoc
  4. Reutiliza tipos: Crea una librería de utility types para tu proyecto
  5. Testing de tipos: Usa herramientas como tsd para validar tipos

Conclusión

Los utility types de TypeScript son herramientas poderosas que te permiten manipular y transformar tipos de forma elegante. Dominarlos te ayudará a escribir código más seguro, mantenible y expresivo.