typescript - 19 de abril

Utility Types en TypeScript

Aprende sobre los Utility Types en TypeScript y cómo usarlos para mejorar tu código.

Los Utility Types en TypeScript son tipos genéricos que ya vienen incluidos en el lenguaje y nos ayudan a transformar tipos existentes sin tener que reescribirlos.

Si trabajas con DTOs, formularios, respuestas de API o validaciones, estos tipos te ahorran muchísimo tiempo.

Partial<T>

Hace que todas las propiedades de un tipo sean opcionales.

interface User {
  id: number;
  name: string;
  email: string;
  isAdmin: boolean;
}

function updateUser(id: number, fieldsToUpdate: Partial<User>) {
  console.log(`Actualizando usuario ${id} con:`, fieldsToUpdate);
}

updateUser(1, { email: "nuevo@correo.com" });
updateUser(2, { name: "Ana", isAdmin: false });

Required<T>

Hace que todas las propiedades de un tipo (incluso las opcionales) sean requeridas.

interface AppConfig {
  port?: number;
  databaseUrl?: string;
  environment: "development" | "production";
}

function finalizeConfig(config: AppConfig): Required<AppConfig> {
  return {
    port: config.port ?? 3000,
    databaseUrl: config.databaseUrl ?? "mongodb://localhost/db",
    environment: config.environment,
  };
}

const finalConfig = finalizeConfig({ environment: "production" });
console.log("Configuración final:", finalConfig);

Omit<T, K>

Crea un tipo eliminando propiedades de otro tipo.

interface UserWithPassword {
  id: number;
  name: string;
  email: string;
  passwordHash: string;
}

type PublicUser = Omit<UserWithPassword, "passwordHash">;

const publicProfile: PublicUser = {
  id: 1,
  name: "Carlos",
  email: "carlos@correo.com",
};

Pick<T, K>

Crea un tipo seleccionando solo ciertas propiedades.

interface BlogPost {
  id: string;
  title: string;
  content: string;
  author: string;
  views: number;
  tags: string[];
}

type PostPreview = Pick<BlogPost, "title" | "author" | "tags">;

const previewData: PostPreview = {
  title: "Aprende Utility Types",
  author: "Wickz",
  tags: ["typescript", "desarrollo-web"],
};

Readonly<T>

Hace que las propiedades sean de solo lectura.

interface AppSettings {
  apiUrl: string;
  theme: "light" | "dark";
}

const settings: Readonly<AppSettings> = {
  apiUrl: "/api/v1",
  theme: "dark",
};

// settings.theme = "light"; // Error

Exclude<T, U>

Elimina de una unión los tipos asignables a U.

type RequestStatus = "loading" | "success" | "error";
type FinalStatus = Exclude<RequestStatus, "loading">;

const status: FinalStatus = "success";

Extract<T, U>

Hace lo contrario de Exclude: mantiene los tipos que sí coinciden.

type AppEvent = "click" | "hover" | "submit" | "keydown";
type MouseEvent = Extract<AppEvent, "click" | "hover">;

const mouseAction: MouseEvent = "click";

ReturnType<T>

Obtiene el tipo de retorno de una función.

function createAuthResponse(success: boolean) {
  if (success) return { status: 200 as const, token: "abc-123" };
  return { status: 401 as const, error: "Unauthorized" };
}

type AuthResponse = ReturnType<typeof createAuthResponse>;

const response: AuthResponse = { status: 200, token: "xyz-456" };

Parameters<T>

Obtiene los tipos de los parámetros de una función en forma de tupla.

function sendMessage(chatId: string, message: string, urgent?: boolean) {
  return { ok: true, chatId, message, urgent };
}

type MessageParams = Parameters<typeof sendMessage>;

const messageArgs: MessageParams = ["#general", "Hola equipo", true];
sendMessage(...messageArgs);

NonNullable<T>

Elimina null y undefined de un tipo.

type MaybeString = string | null | undefined;

function processText(text: NonNullable<MaybeString>) {
  console.log(text.trim());
}

const textInput: MaybeString = "  Hola Mundo  ";
if (textInput) {
  processText(textInput);
}

Awaited<T>

Obtiene el tipo real resuelto por una promesa.

async function fetchUserData(id: number) {
  return {
    id,
    name: "Elena",
    email: "elena@correo.com",
    isAdmin: true,
  };
}

type FetchedUser = Awaited<ReturnType<typeof fetchUserData>>;

async function displayUser() {
  const user: FetchedUser = await fetchUserData(10);
  console.log(`Usuario obtenido con Awaited: ${user.name}`);
}

displayUser();

Y listo. Estos son los Utility Types que más se usan en el día a día.

Cuando empieces a combinarlos entre sí (por ejemplo Partial<Pick<T, "x" | "y">>), vas a notar que TypeScript se vuelve mucho más potente sin complicarte el código.