Kaique Mitsuo Silva Yamamoto
Arquitetura softwareFrontend webTypeScriptGenerics Avançado

Utility Types

Partial, Pick, Omit, Record, Extract, Exclude, ReturnType, Parameters — tipos utilitários embutidos e customizados.

Utility Types em TypeScript

TypeScript vem com um conjunto de tipos utilitários (utility types) que transformam tipos existentes. São como funções, mas para tipos.

Tipos utilitários embutidos

Partial<T> — Torna todas as propriedades opcionais

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

type PartialUser = Partial<User>;
// { id?: string; name?: string; email?: string; age?: number; }

// Uso: updates parciais
function updateUser(id: string, data: Partial<User>): Promise<User> {
  // só precisa passar os campos que quer alterar
  return db.users.update(id, data);
}

updateUser('1', { name: 'Novo Nome' }); // ✅ só altera o nome

Required<T> — Torna todas as propriedades obrigatórias

type Config = {
  host?: string;
  port?: number;
  database?: string;
};

type RequiredConfig = Required<Config>;
// { host: string; port: number; database: string; }

// Uso: garantir que configuração está completa
function startServer(config: Required<Config>) {
  // TS garante que host, port e database existem
}

Pick<T, K> — Seleciona propriedades específicas

type UserPublic = Pick<User, 'id' | 'name'>;
// { id: string; name: string; }

// Uso: DTOs públicos que expõem menos dados
app.get('/api/users/:id', async (req, res) => {
  const user = await db.users.findById(req.params.id);
  const publicData: Pick<User, 'id' | 'name' | 'email'> = {
    id: user.id,
    name: user.name,
    email: user.email,
  };
  res.json(publicData);
});

Omit<T, K> — Remove propriedades específicas

type CreateUserDto = Omit<User, 'id'>;
// { name: string; email: string; age: number; }
// id é removido — o banco gera

// Uso: criar sem ID
async function createUser(data: CreateUserDto): Promise<User> {
  const id = generateId();
  return db.users.create({ id, ...data });
}

Record<K, V> — Cria tipo de objeto com chaves e valores fixos

type StatusMap = Record<string, string>;
// { [key: string]: string }

// Mais específico
type HttpCodes = Record<'ok' | 'created' | 'notFound' | 'error', number>;
const codes: HttpCodes = {
  ok: 200,
  created: 201,
  notFound: 404,
  error: 500,
};

// Frontend — configuração de rotas
type RouteConfig = Record<string, { path: string; component: React.ComponentType }>;

Extract<T, U> e Exclude<T, U> — Filtra union types

type AllStatus = 'active' | 'inactive' | 'pending' | 'deleted' | 'banned';

type ActiveStatuses = Extract<AllStatus, 'active' | 'pending'>;
// 'active' | 'pending'

type NonDeletedStatuses = Exclude<AllStatus, 'deleted' | 'banned'>;
// 'active' | 'inactive' | 'pending'

ReturnType<T> e Parameters<T> — Deriva de funções

function getUser(id: string): { name: string; email: string } {
  return db.users.findById(id);
}

type UserReturn = ReturnType<getUser>;
// { name: string; email: string }

type UserParams = Parameters<getUser>;
// [id: string]

// Uso: manter tipos sincronizados automaticamente
type FetchResult = ReturnType<typeof fetchUser>;
// Se fetchUser mudar o retorno, FetchResult atualiza sozinho

Awaited<T> — Extrai o tipo de uma Promise

type UserPromise = Promise<User>;
type UserResolved = Awaited<UserPromise>;
// User

// Útil para tipar o resultado de operações async sem duplicar
async function loadDashboard() {
  const user = await fetchUser();     // User
  const orders = await fetchOrders(); // Order[]
  const stats = await fetchStats();   // Stats
  return { user, orders, stats };
}

type DashboardData = Awaited<ReturnType<typeof loadDashboard>>;
// { user: User; orders: Order[]; stats: Stats }

Utility types customizados

Nullable<T> — Adiciona null e undefined

type Nullable<T> = T | null | undefined;

function findUser(id: string): Nullable<User> {
  return db.users.findById(id) ?? undefined;
}

DeepPartial<T> — Partial recursivo

type DeepPartial<T> = {
  [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};

interface Config {
  server: {
    host: string;
    port: number;
    ssl: {
      enabled: boolean;
      cert: string;
    };
  };
  db: {
    url: string;
    poolSize: number;
  };
}

type PartialConfig = DeepPartial<Config>;
// server.ssl.enabled pode ser definido sem definir server.host

const override: PartialConfig = {
  server: { ssl: { enabled: true } }, // ✅ TS aceita — não precisa definir host nem port
};

NonNullableProperties<T> — Remove null de todas as propriedades

type NonNullableProperties<T> = {
  [K in keyof T]: NonNullable<T[K]>;
};

interface FormState {
  name: string | null;
  email: string | null;
  age: number | null;
}

type ValidatedForm = NonNullableProperties<FormState>;
// { name: string; email: string; age: number; }

function validateForm(form: FormState): ValidatedForm {
  if (!form.name || !form.email || !form.age) {
    throw new Error('Todos os campos são obrigatórios');
  }
  return form as ValidatedForm; // ✅ TS sabe que nada é null
}

Referências

On this page