Tipos utilitários do TypeScript com Claude Code: guia prático
Aprenda Pick, Omit, Partial, Record, ReturnType e Awaited com exemplos executáveis para Claude Code.
Tipos utilitários do TypeScript são ferramentas para transformar um tipo existente em outro tipo com uma finalidade específica.
Se você copia manualmente um tipo User para criar versões de visualização pública, formulário e atualização de API, essas cópias divergem quando um campo muda.
Claude Code ajuda na refatoração, mas você precisa entender a intenção de cada utilitário para revisar o resultado.
Este guia explica Pick, Omit, Partial, Required, Readonly, Record, ReturnType e Awaited de forma simples.
Depois mostra como pedir a Claude Code uma modelagem prática, com exemplos que podem ser copiados e executados.
Use o TypeScript Handbook Utility Types como fonte oficial e combine com TSConfig strict para encontrar erros mais cedo.
Para continuar no ClaudeCodeLab, leia também dicas práticas de TypeScript e o guia de generics em TypeScript.
Modelo mental simples
Um tipo utilitário é uma operação segura de “copiar e remodelar” tipos. É como duplicar uma planilha, remover colunas, marcar algumas como opcionais e usar essa versão em outro fluxo. A diferença é que o TypeScript verifica essa transformação antes da execução.
Pick<User, "id" | "name"> seleciona apenas id e name de User.
Omit<User, "passwordHash"> mantém quase tudo, mas remove passwordHash.
Use Pick quando o tipo de destino for pequeno e explícito.
Use Omit quando o destino for quase igual ao original e só alguns campos perigosos ou irrelevantes precisarem sair.
Partial<User> torna todas as propriedades opcionais.
É útil para rascunhos, PATCH e formulários em andamento, mas pode transformar um email obrigatório em opcional durante a criação de conta.
Required<User> faz o oposto e torna propriedades opcionais obrigatórias.
Readonly<User> impede reatribuição e documenta que uma configuração não deve mudar.
Record<Keys, Type> cria um dicionário com chaves conhecidas.
ReturnType<typeof fn> extrai o tipo de retorno de uma função.
Awaited<Promise<T>> extrai o valor recebido depois de await.
Combinados, Awaited e ReturnType mantêm funções de API e tipos de UI sincronizados.
flowchart LR
A["Source type: User"] --> B["Pick: public view"]
A --> C["Omit: remove secrets"]
A --> D["Partial: update input"]
A --> E["Required: validated input"]
A --> F["Readonly: fixed settings"]
G["Function"] --> H["ReturnType"]
I["Promise"] --> J["Awaited"]
Comparação rápida
| Tipo | O que faz | Uso prático | Atenção |
|---|---|---|---|
Pick<T, K> | Seleciona chaves específicas | Listas, perfis públicos, cards | Campos não escolhidos não existem |
Omit<T, K> | Remove chaves específicas | Inputs de criação, saída pública, logs | Não remove valores em runtime |
Partial<T> | Torna todas as chaves opcionais | Rascunhos, PATCH, formulários parciais | É superficial |
Required<T> | Torna todas as chaves obrigatórias | Dados validados antes de salvar | Pode exigir campos cedo demais |
Readonly<T> | Impede reatribuição | Configurações, permissões, constantes | Objetos aninhados precisam de cuidado |
Record<K, T> | Cria dicionário de chaves fixas | Permissões por papel, labels, preços | Record<string, T> costuma ser amplo demais |
ReturnType<T> | Extrai retorno de função | Sincronizar API e UI | Use typeof functionName |
Awaited<T> | Extrai valor resolvido de Promise | Resultados de funções async | Não é await em runtime |
Cole esta tabela no Claude Code e peça uma revisão da escolha dos tipos utilitários contra o objetivo real.
Em projetos com strict: true, tipos vagos quebram mais cedo, o que reduz custo de correção.
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"skipLibCheck": true
}
}
Caso 1: derivar tipos de UI e formulário a partir de User
Telas administrativas geralmente precisam de várias versões da mesma entidade. O tipo do banco, o tipo público e o tipo do formulário não devem ser cópias mantidas à mão. Mantenha um tipo fonte e derive o resto com utilitários.
type UserRole = "admin" | "editor" | "viewer";
interface User {
id: string;
name: string;
email: string;
role: UserRole;
bio: string;
passwordHash: string;
createdAt: Date;
updatedAt: Date;
}
type PublicUser = Pick<User, "id" | "name" | "role" | "bio">;
type UserDraft = Partial<Omit<User, "id" | "passwordHash" | "createdAt" | "updatedAt">>;
type CreateUserInput =
Required<Pick<User, "name" | "email" | "role">> &
Partial<Pick<User, "bio">>;
function buildCreatePayload(input: CreateUserInput): Omit<User, "id" | "createdAt" | "updatedAt"> {
return {
name: input.name,
email: input.email,
role: input.role,
bio: input.bio ?? "",
passwordHash: "hashed-by-server",
};
}
const publicUser: PublicUser = {
id: "u_001",
name: "Masa",
role: "admin",
bio: "Claude Code workflow designer",
};
const draft: UserDraft = {
name: "Draft user",
bio: "Saved before email is confirmed",
};
console.log(publicUser);
console.log(buildCreatePayload({ name: "Aki", email: "aki@example.com", role: "editor" }));
console.log(draft);
A instrução para Claude Code deve separar o que fica, o que sai e quando algo é obrigatório.
Create public display, form draft, and create-API types from the User type.
Do not expose passwordHash.
For creation, require only name, email, and role. Keep bio optional.
Use Pick/Omit/Partial/Required and briefly explain why each utility type is used.
Em produção, combine esses tipos com validação em runtime, como Zod. Tipos utilitários conferem a forma antes da execução; eles não provam que dados enviados por usuários são confiáveis.
Caso 2: travar recursos por plano com Record
Planos de preço, papéis e tabelas de status combinam muito bem com Record.
O compilador consegue apontar se o plano team ficou sem configuração ou se prioritySupport foi escrito errado.
type Plan = "free" | "pro" | "team";
type Feature = "exportPdf" | "inviteMember" | "prioritySupport";
const featureMatrix: Readonly<Record<Plan, Readonly<Record<Feature, boolean>>>> = {
free: {
exportPdf: false,
inviteMember: false,
prioritySupport: false,
},
pro: {
exportPdf: true,
inviteMember: false,
prioritySupport: false,
},
team: {
exportPdf: true,
inviteMember: true,
prioritySupport: true,
},
};
function canUse(plan: Plan, feature: Feature): boolean {
return featureMatrix[plan][feature];
}
console.log(canUse("pro", "exportPdf"));
console.log(canUse("free", "prioritySupport"));
Readonly registra que essa matriz não deve ser alterada durante a execução.
Como ele é superficial, o exemplo também marca o Record interno como readonly.
Para estruturas mais profundas, use as const ou um helper deep readonly do projeto.
Caso 3: reutilizar resultados de API com ReturnType e Awaited
Quando tipos do cliente API e da UI são escritos separadamente, mudanças na resposta ficam caras.
ReturnType mais Awaited deriva o tipo diretamente da função async real.
async function fetchInvoice(invoiceId: string) {
return {
id: invoiceId,
status: "paid" as const,
amount: 48000,
currency: "JPY" as const,
paidAt: new Date("2026-06-02T10:00:00+09:00"),
};
}
type Invoice = Awaited<ReturnType<typeof fetchInvoice>>;
type InvoiceSummary = Pick<Invoice, "id" | "status" | "amount" | "currency">;
function formatInvoice(invoice: InvoiceSummary): string {
return `${invoice.id}: ${invoice.amount.toLocaleString()} ${invoice.currency} (${invoice.status})`;
}
async function main() {
const invoice = await fetchInvoice("inv_20260602");
console.log(formatInvoice(invoice));
}
main();
Quando a função API é a fronteira confiável, peça ao Claude Code para não escrever outro tipo de resposta manual. Se a fronteira for uma API externa ou input de usuário, mantenha validação em runtime e tratamento de erro.
Caso 4: usar Partial no nível correto
Partial<T> é superficial.
Ele não torna automaticamente opcionais os campos dentro de objetos aninhados, um erro comum para iniciantes.
interface Profile {
id: string;
displayName: string;
settings: {
emailNotification: boolean;
smsNotification: boolean;
};
}
type ProfilePatch =
Omit<Partial<Profile>, "settings"> & {
settings?: Partial<Profile["settings"]>;
};
function patchProfile(current: Profile, patch: ProfilePatch): Profile {
return {
...current,
...patch,
settings: {
...current.settings,
...patch.settings,
},
};
}
const profile: Profile = {
id: "p_001",
displayName: "Masa",
settings: {
emailNotification: true,
smsNotification: false,
},
};
console.log(patchProfile(profile, { settings: { smsNotification: true } }));
Com apenas ProfilePatch = Partial<Profile>, atualizar settings ainda exigiria o objeto settings completo.
Em equipes com pessoas iniciantes, um tipo concreto como esse costuma ser mais legível do que um deep partial genérico.
Falhas concretas para evitar
Omit remove uma chave do tipo, não do objeto em runtime.
Se você retorna logs ou respostas públicas, precisa remover de fato o valor sensível.
interface Account {
id: string;
email: string;
passwordHash: string;
}
type SafeAccount = Omit<Account, "passwordHash">;
function toSafeAccount(account: Account): SafeAccount {
const { passwordHash, ...safeAccount } = account;
return safeAccount;
}
console.log(toSafeAccount({
id: "a_001",
email: "masa@example.com",
passwordHash: "secret",
}));
Record<string, T> costuma ser amplo demais para regras de negócio.
Se as chaves permitidas são conhecidas, prefira uma união como type Plan = "free" | "pro" | "team".
Required<T> cedo demais deixa o formulário pesado.
Use depois da validação, quando os dados realmente estiverem completos.
Awaited<T> descreve apenas o tipo resolvido de uma Promise.
Ele não espera em runtime; você ainda precisa de await ou .then().
Se essa diferença se perder, Claude Code pode arrumar os tipos e esquecer estados de carregamento e erro.
Prompt de revisão para Claude Code
Depois da implementação, peça uma revisão focada em risco.
Review this TypeScript type design.
1. Are Pick/Omit/Partial/Required/Readonly/Record matched to their use cases?
2. Are secrets removed at runtime, not only with Omit?
3. Does Partial make create inputs too loose?
4. Do ReturnType and Awaited reduce duplicated API types?
5. Are any vague any or broad string types left under strict settings?
Essa formulação evita uma revisão superficial de “deixar os tipos bonitos”.
Masa passou por isso em uma tela administrativa pequena: usar Partial em todos os lugares fazia um email vazio parecer aceitável até a etapa de salvar.
Ao separar “rascunho”, “criação” e “salvo” em tipos derivados, os patches do Claude Code ficaram muito mais fáceis de revisar.
Conclusão
Tipos utilitários do TypeScript não são acrobacia de tipos.
Eles servem para expressar diferenças entre dados públicos, rascunhos, entradas validadas, configuração fixa e resultados async sem duplicar definições.
Use Pick e Omit para controlar campos, Partial e Required para modelar etapas, Readonly e Record para proteger configuração, e ReturnType mais Awaited para reduzir tipos de API duplicados.
ClaudeCodeLab ajuda equipes a aplicar Claude Code em arquitetura TypeScript, CMS de conteúdo, ferramentas internas e funis monetizados. Se seu projeto tem tipos mas ainda entrega erros evitáveis, peça treinamento e consultoria em Claude Code.
Testei os exemplos deste artigo com uma mentalidade de TypeScript estrito.
O maior ganho prático veio de ReturnType e Awaited: quando a resposta da API muda, os pontos de ajuste na UI aparecem mais cedo.
A ressalva importante é Omit: ele nunca remove segredos em runtime, então funções de resposta pública ainda precisam remover propriedades explicitamente.
PDF grátis: cheatsheet do Claude Code
Informe seu e-mail e baixe uma página com comandos, hábitos de revisão e workflows seguros.
Cuidamos dos seus dados e não enviamos spam.
Sobre o autor
Masa
Engenheiro focado em workflows práticos com Claude Code.
Artigos relacionados
Escada de segurança de permissões no Claude Code
Amplie de read-only para edições limitadas, comandos de prova e deploy checks sem perder controle.
Claude Code Small PR Proof Pack: pequenas mudanças fáceis de revisar
Um pacote de prova para PRs do Claude Code: diff, checks, URL pública, CTA e rollback.
Gate de revisão antes do commit com Claude Code
Revisão antes do commit com Claude Code: diff, build, URL pública, Gumroad, consultoria, testes e arquivos fora do escopo.