Utility Types no TypeScript:
Domine os Utility Types do TypeScript. De Partial e Pick até ReturnType e Extract, tudo com exemplos práticos que você usa em projetos reais.
Por que isso é importante
Utility Types no TypeScript:. Domine os Utility Types do TypeScript. De Partial e Pick até ReturnType e Extract, tudo com exemplos práticos que você usa em projetos reais.
O Que São Utility Types
Utility Types são tipos genéricos que o TypeScript já traz prontos. Eles recebem um tipo existente e devolvem um novo tipo transformado. Pensa neles como funções, mas pra tipos.
O TypeScript tem mais de 20 Utility Types nativos. Os mais usados no dia a dia são: Partial, Required, Pick, Omit, Record, Readonly, ReturnType, Parameters, Exclude e Extract. Cada um resolve um problema específico.
A sacada é que eles compõem. Dá pra combinar Partial com Pick, ou Omit com Required. Isso te dá flexibilidade pra criar tipos complexos sem escrever tudo do zero.
Na prática, você vai usar Utility Types em três cenários principais: formulários (campos opcionais), APIs (selecionar campos da resposta) e estado (versões parciais de objetos). Vamos ver cada um.
Como Usar Utility Types Passo a Passo
Aqui vai o caminho pra dominar cada Utility Type na ordem certa.
Exemplos Práticos de Cada Utility Type
Vamos ver cada Utility Type com código real. Começando pelo tipo base que vamos transformar.
Tipo Base e Partial / Required
// Tipo base que vamos transformar em todos os exemplos
interface User {
id: number;
name: string;
email: string;
age: number;
role: 'admin' | 'user';
}
// Partial<T> - tudo fica opcional
type UpdateUserDTO = Partial<User>;
// Resultado:
// { id?: number; name?: string; email?: string; age?: number; role?: 'admin' | 'user' }
function updateUser(id: number, data: Partial<User>): User {
// Recebe so os campos que mudaram
return { ...getCurrentUser(id), ...data };
}
updateUser(1, { name: 'Novo Nome' }); // OK! So passou name
updateUser(1, { age: 30, role: 'admin' }); // OK! Passou age e role
// Required<T> - tudo fica obrigatorio
interface Config {
host?: string;
port?: number;
debug?: boolean;
}
type StrictConfig = Required<Config>;
// Agora host, port e debug sao obrigatorios
const config: StrictConfig = {
host: 'localhost',
port: 3000,
debug: false, // nao pode omitir nenhum
};
Pick e Omit
// Pick<T, K> - seleciona propriedades
type UserPreview = Pick<User, 'name' | 'email'>;
// Resultado: { name: string; email: string }
// Omit<T, K> - remove propriedades
type CreateUserDTO = Omit<User, 'id'>;
// Resultado: { name: string; email: string; age: number; role: 'admin' | 'user' }
// Combinando: selecionar campos e tornar opcionais
type UserUpdateForm = Partial<Pick<User, 'name' | 'email' | 'age'>>;
// Resultado: { name?: string; email?: string; age?: number }
// Uso pratico em componentes
function UserCard({ name, email }: Pick<User, 'name' | 'email'>) {
return `${name} - ${email}`;
}
// Uso pratico em API
function createUser(data: Omit<User, 'id'>): User {
return { id: generateId(), ...data };
}
Record e Readonly
// Record<K, V> - cria mapa tipado
type UserRole = 'admin' | 'user' | 'editor';
type RolePermissions = Record<UserRole, string[]>;
const permissions: RolePermissions = {
admin: ['read', 'write', 'delete'],
user: ['read'],
editor: ['read', 'write'],
};
// Record com objetos complexos
type UserMap = Record<string, User>;
const users: UserMap = {
'user-1': { id: 1, name: 'Ana', email: 'ana@dev.com', age: 28, role: 'admin' },
'user-2': { id: 2, name: 'Carlos', email: 'carlos@dev.com', age: 32, role: 'user' },
};
// Readonly<T> - impede alteracao
type ImmutableUser = Readonly<User>;
const admin: ImmutableUser = {
id: 1, name: 'Admin', email: 'admin@app.com', age: 35, role: 'admin'
};
// admin.name = 'Outro'; // Error! Cannot assign to 'name' because it is a read-only property
ReturnType e Parameters
// ReturnType<T> - extrai tipo de retorno de funcao
function fetchUser(id: number) {
return { id, name: 'Ana', email: 'ana@dev.com', active: true };
}
type FetchUserResult = ReturnType<typeof fetchUser>;
// Resultado: { id: number; name: string; email: string; active: boolean }
// Parameters<T> - extrai tipos dos parametros
function createOrder(userId: number, items: string[], total: number) {
// ...
}
type CreateOrderParams = Parameters<typeof createOrder>;
// Resultado: [userId: number, items: string[], total: number]
// Pegar um parametro especifico
type FirstParam = Parameters<typeof createOrder>[0]; // number
type SecondParam = Parameters<typeof createOrder>[1]; // string[]
// Uso pratico: wrapper de funcao
function logAndCreate(...args: Parameters<typeof createOrder>) {
console.log('Criando order com:', args);
return createOrder(...args);
}
Exclude e Extract
// Exclude<T, U> - remove membros de union
type Status = 'loading' | 'success' | 'error' | 'idle';
type ActiveStatus = Exclude<Status, 'idle'>;
// Resultado: 'loading' | 'success' | 'error'
type NonErrorStatus = Exclude<Status, 'error' | 'loading'>;
// Resultado: 'success' | 'idle'
// Extract<T, U> - mantem so os que batem
type NumericValues = Extract<string | number | boolean, number | boolean>;
// Resultado: number | boolean
// Uso pratico: filtrar eventos
type AppEvent =
| { type: 'click'; x: number; y: number }
| { type: 'scroll'; offset: number }
| { type: 'keypress'; key: string };
type MouseEvent = Extract<AppEvent, { type: 'click' }>;
// Resultado: { type: 'click'; x: number; y: number }
type NonMouseEvent = Exclude<AppEvent, { type: 'click' }>;
// Resultado: { type: 'scroll'; offset: number } | { type: 'keypress'; key: string }
Cada Utility Type resolve um problema específico. A mágica acontece quando você combina vários pra criar exatamente o tipo que precisa, sem repetir nenhuma linha de definição.
Erros Comuns com Utility Types
Armadilhas que travam seu código
Confundir Pick com Omit: Pick seleciona O QUE você quer. Omit remove O QUE você NÃO quer. Se o tipo tem 20 campos e você quer 18, use Omit pra remover 2. Se quer só 3, use Pick.
Partial recursivo não existe nativamente: Partial só torna o primeiro nível opcional. Objetos aninhados continuam obrigatórios. Pra Partial profundo, você precisa criar um tipo recursivo customizado.
Record com chaves não restritivas: Record<string, T> aceita QUALQUER string como chave. Se você tem um conjunto fixo de chaves, use um union type: Record<'a' | 'b' | 'c', T>.
Esquecer typeof antes de ReturnType: ReturnType espera um tipo de função, não a função em si. Use ReturnType<typeof minhaFuncao>, não ReturnType<minhaFuncao>.
Exclude em tipos que não são union: Exclude só funciona com union types. Tentar usar em um tipo de objeto não faz o que você espera. Pra remover campos de objeto, use Omit.
Checklist de Utility Types
Domine Utility Types em Projetos Reais
Utility Types são o que separam tipagem amadora de tipagem profissional. No CrazyStack, você usa Partial pra DTOs, Pick pra componentes, Omit pra APIs e Record pra configurações. Tudo num projeto real com Node.js, React e TypeScript.
Chega de copiar interfaces inteiras pra mudar um campo. Você termina o curso dominando composição de tipos e com um projeto pronto pra produção.
Continue lendo
Como Usar Conditional Types no TypeScript
Domine tipos condicionais no TypeScript com extends, never e distribuição.
Como Usar Mapped Types no TypeScript
Mapped types com [K in keyof T], modifiers e key remapping.
Como Usar Generics no TypeScript
Domine generics no TypeScript e crie código reutilizável e tipado.
Interface no TypeScript
Quando usar interface