Utility types são tipos genéricos embutidos no TypeScript que transformam um tipo em outro. Em vez de escrever um segundo tipo User com todos os campos opcionais, você escreve Partial<User>; em vez de copiar três campos, Pick<User, "id" | "name">. Eles são globais, então não é preciso importar nada.
Quando User muda, os quatro tipos derivados acompanham. A biblioteca padrão do TypeScript (lib.es5.d.ts) declara 22 utility types. As seções abaixo listam todos eles, agrupados pelo tipo de tipo com que trabalham, com um link para a página detalhada quando ela existe.
Tipos de objeto: Partial, Required, Readonly, Pick, Omit, Record
| Utility type | O que faz | Exemplo |
|---|---|---|
Partial<T> | torna toda propriedade opcional | Partial<User> para um payload de atualização |
Required<T> | torna toda propriedade obrigatória (remove o ?) | Required<Config> depois de aplicar os padrões |
Readonly<T> | torna toda propriedade readonly | Readonly<State> |
Pick<T, K> | mantém só as chaves K | Pick<User, "id" | "name"> |
Omit<T, K> | remove as chaves K | Omit<User, "password"> |
Record<K, V> | um tipo de objeto com chaves K e valores V | Record<"en" | "de", string> |
Required<T> é o oposto de Partial<T>; a página de Partial cobre os dois, incluindo o undefined explícito que um spread como este deixa passar.
Union types: Exclude, Extract, NonNullable
| Utility type | O que faz | Exemplo |
|---|---|---|
Exclude<U, M> | remove os membros da union atribuíveis a M | Exclude<"a" | "b" | "c", "a"> é "b" | "c" |
Extract<U, M> | mantém os membros da union atribuíveis a M | Extract<string | number, number> é number |
NonNullable<T> | remove null e undefined | NonNullable<string | null> é string |
Esses três trabalham com unions, não com objetos. Essa é a diferença principal em relação a Pick e Omit, que recebem um tipo de objeto e uma lista das chaves dele.
Tipos de função e de classe: Parameters, ReturnType e outros
| Utility type | O que faz | Exemplo |
|---|---|---|
ReturnType<F> | o tipo de retorno de um tipo de função | ReturnType<typeof createStore> |
Parameters<F> | os tipos dos parâmetros como uma tupla | Parameters<typeof fetchPage>[0] |
ConstructorParameters<C> | os parâmetros do construtor de uma classe como uma tupla | ConstructorParameters<typeof Point> |
InstanceType<C> | o tipo da instância que um construtor cria | InstanceType<typeof Point> |
ThisParameterType<F> | o tipo do parâmetro this de uma função | ThisParameterType<typeof greet> |
OmitThisParameter<F> | o tipo da função sem o parâmetro this | o tipo de greet.bind(obj) |
ThisType<T> | define o tipo de this dentro dos métodos de um object literal | usado com noImplicitThis em APIs de builder |
NoInfer<T> | impede que um parâmetro de tipo seja inferido a partir desta posição | fallback: NoInfer<C> |
typeof createOrder é necessário porque esses utilities recebem um tipo, e createOrder é um valor. O mesmo vale para classes: typeof Point é o tipo do construtor, enquanto Point sozinho, como tipo, já significa o tipo da instância.
NoInfer controla de onde um generic tira o seu tipo:
Sem NoInfer, o TypeScript inferiria C a partir dos dois argumentos e o ampliaria para "red" | "green" | "blue", então o erro de digitação no fallback seria aceito.
Tipos de string: Uppercase, Lowercase, Capitalize, Uncapitalize
| Utility type | O que faz | Exemplo |
|---|---|---|
Uppercase<S> | passa um string literal type para maiúsculas | Uppercase<"get"> é "GET" |
Lowercase<S> | passa para minúsculas | Lowercase<"GET"> é "get" |
Capitalize<S> | passa o primeiro caractere para maiúscula | Capitalize<"name"> é "Name" |
Uncapitalize<S> | passa o primeiro caractere para minúscula | Uncapitalize<"Name"> é "name" |
Esses quatro são embutidos no compilador em vez de escritos em TypeScript, e são mais úteis dentro de template literal types, como `on${Capitalize<E>}` para nomes de handlers de eventos.
Promises: Awaited
| Utility type | O que faz | Exemplo |
|---|---|---|
Awaited<T> | o tipo que você obtém com await, desembrulhando promises aninhadas | Awaited<Promise<Promise<number>>> é number |
Awaited<ReturnType<typeof fn>> é o jeito padrão de dar nome ao tipo do resultado de uma função async sem declará-lo separadamente.
Combinando utility types
Utility types se aninham. Algumas combinações aparecem com frequência suficiente para valer a pena saber de cor:
Leia um utility type aninhado de dentro para fora: Readonly<Pick<Post, "id" | "title">> primeiro mantém duas propriedades e depois as torna somente leitura. As mesmas peças montam um helper PartialBy que torna opcionais só algumas chaves; a página de Partial mostra ele completo.
Utility types não fazem nada em tempo de execução
Todo utility type é apagado quando o código é compilado. Um valor com tipo Omit<User, "password"> ainda pode carregar uma senha em tempo de execução se o objeto de onde veio tinha uma:
O tipo só limita o que o seu código tem permissão para ler. Para remover um campo dos dados, tire-o com desestruturação como nas últimas linhas, e para impedir mutação em tempo de execução use Object.freeze, não Readonly. Os tipos embutidos são mapped types e conditional types de uma linha, então as mesmas ferramentas permitem escrever os seus.
Perguntas frequentes
O que são utility types no TypeScript?
Tipos genéricos que vêm com o TypeScript e transformam outros tipos: Partial<T> torna toda propriedade opcional, Pick<T, K> mantém algumas propriedades, ReturnType<F> pega o tipo de retorno de uma função, e assim por diante. Eles são declarados na biblioteca padrão, então você os usa sem importar nada.
Preciso importar os utility types?
Não. Partial, Omit, Record, ReturnType e os demais são tipos globais dos arquivos de biblioteca embutidos no TypeScript. Escreva Partial<User> em qualquer lugar; não precisa de import nem de pacote npm.
Quais utility types vêm embutidos no TypeScript?
22, todos declarados em lib.es5.d.ts: Partial, Required, Readonly, Pick, Omit, Record, Exclude, Extract, NonNullable, Parameters, ConstructorParameters, ReturnType, InstanceType, ThisParameterType, OmitThisParameter, ThisType, NoInfer, Awaited, Uppercase, Lowercase, Capitalize e Uncapitalize.
Utility types mudam objetos em tempo de execução?
Não. Eles só descrevem tipos e são apagados do JavaScript gerado. Omit<User, "password"> não apaga uma propriedade password, e Readonly<T> não congela nada. Para mudar o objeto de verdade, escreva o código: um padrão rest na desestruturação, Object.freeze, e assim por diante.
Posso escrever meus próprios utility types?
Sim. Os embutidos são TypeScript comum: a maioria é um mapped type ou um conditional type de uma linha em lib.es5.d.ts. type Nullable<T> = { [K in keyof T]: T[K] | null } é um utility type personalizado escrito do mesmo jeito.