Template Literal Types no TypeScript 2026
Domine template literal types no TypeScript. Crie tipos string dinâmicos, padrões de evento e chaves geradas automaticamente com exemplos do mundo real.
O Que São Template Literal Types
Template literal types usam a mesma sintaxe de template strings do JavaScript, mas no nível dos tipos. Em vez de montar strings em runtime, você monta tipos de string em tempo de compilação. A sintaxe é ${Type} dentro de um tipo.
Quando você escreve type Saudacao = Olá, ${string}, o TypeScript aceita qualquer string que comece com 'Olá, '. Se escrever ${"get" | "set"}${string}, aceita qualquer string que comece com get ou set. O compilador verifica isso antes do código executar.
A mágica aparece quando você combina template literals com union types. Se você tem type Cor = 'red' | 'blue' e type Tamanho = 'sm' | 'lg', o tipo ${Cor}-${Tamanho} gera automaticamente 'red-sm' | 'red-lg' | 'blue-sm' | 'blue-lg'. O TypeScript faz o produto cartesiano sozinho.
O TypeScript ainda traz 4 utility types de manipulação de string: Uppercase, Lowercase, Capitalize e Uncapitalize. Eles transformam tipos string literais. Uppercase<'hello'> vira 'HELLO'. Isso abre portas pra gerar nomes de eventos, getters, setters e rotas de forma automática.
Como Usar Template Literal Types Passo a Passo
Vamos construir do mais simples ao mais sofisticado. Cada passo desbloqueia um padrão novo.
- Passo 1 - Crie tipos string com template: Use backticks no nível do tipo: type Endpoint =
/api/${string}. Agora qualquer valor atribuído a esse tipo precisa começar com /api/. Simples assim. - Passo 2 - Combine unions com templates: type Acao = 'click' | 'hover'. type Elemento = 'button' | 'link'. type Evento =
${Acao}_${Elemento}gera 4 combinações automaticamente. Sem escrever nada na mão. - Passo 3 - Use os utility types de string: Uppercase<'hello'> vira 'HELLO'. Capitalize<'name'> vira 'Name'. Use pra gerar variações padronizadas de nomes.
- Passo 4 - Gere nomes de getter/setter: type Getter
= get${Capitalize. Getter<'name'> vira 'getName'. Isso é padrão em frameworks e ORMs.} - Passo 5 - Monte padrões de evento: type EventHandler
= on${Capitalize. EventHandler<'name'> vira 'onNameChange'. Tipagem perfeita pra formulários e estados.}Change - Passo 6 - Combine com mapped types: Use template literals dentro de mapped types pra gerar objetos inteiros com chaves dinâmicas. Esse é o nível avançado que frameworks usam internamente.
Exemplos Práticos de Template Literal Types
Hora de ver código. Cada exemplo resolve um problema concreto de tipagem.
Tipos String Básicos com Template
// Template literal type básico
type ApiRoute = `/api/${string}`;
const rota1: ApiRoute = "/api/users"; // OK
const rota2: ApiRoute = "/api/posts/123"; // OK
// const rota3: ApiRoute = "/users"; // Error!
// Combinando unions: produto cartesiano automático
type Cor = "red" | "green" | "blue";
type Tamanho = "sm" | "md" | "lg";
type ClasseCSS = `${Cor}-${Tamanho}`;
// Resultado: "red-sm" | "red-md" | "red-lg" |
// "green-sm" | "green-md" | "green-lg" |
// "blue-sm" | "blue-md" | "blue-lg"
const classe: ClasseCSS = "red-lg"; // OK
// const errada: ClasseCSS = "red-xl"; // Error!
Utility Types de Manipulação de String
// Uppercase: tudo maiúsculo
type A = Uppercase<"hello">; // "HELLO"
type B = Uppercase<"world">; // "WORLD"
// Lowercase: tudo minúsculo
type C = Lowercase<"HELLO">; // "hello"
// Capitalize: primeira letra maiúscula
type D = Capitalize<"name">; // "Name"
type E = Capitalize<"email">; // "Email"
// Uncapitalize: primeira letra minúscula
type F = Uncapitalize<"Name">; // "name"
// Combinando com template literals
type Campo = "name" | "email" | "age";
type Getter = `get${Capitalize<Campo>}`;
// "getName" | "getEmail" | "getAge"
type Setter = `set${Capitalize<Campo>}`;
// "setName" | "setEmail" | "setAge"
type EnvVar = `APP_${Uppercase<Campo>}`;
// "APP_NAME" | "APP_EMAIL" | "APP_AGE"
Padrões de Eventos Tipados
// Gerando nomes de handler a partir de campos
type FormField = "name" | "email" | "password";
type
// "onNameChange" | "onEmailChange" | "onPasswordChange"
type
// "onNameBlur" | "onEmailBlur" | "onPasswordBlur"
// Tipando um sistema de eventos completo
type EventName = "click" | "hover" | "focus" | "blur";
type ElementId = "submit-btn" | "cancel-btn" | "search-input";
type EventKey = `${ElementId}:${EventName}`;
// "submit-btn:click" | "submit-btn:hover" | ... (16 combinações)
const handlers: Record<EventKey, () => void> = {
"submit-btn:click": () => console.log("submit"),
"submit-btn:hover": () => console.log("hover"),
// ... TypeScript exige todas as 16 chaves
};
Gerando Tipos de Objeto com Chaves Dinâmicas
// Gerando getters e setters tipados
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
interface User {
name: string;
age: number;
active: boolean;
}
type UserGetters = Getters<User>;
// {
// getName: () => string;
// getAge: () => number;
// getActive: () => boolean;
// }
type UserSetters = Setters<User>;
// {
// setName: (value: string) => void;
// setAge: (value: number) => void;
// setActive: (value: boolean) => void;
// }
// Combinando tudo
type UserAccessors = Getters<User> & Setters<User>;
Rotas de API Tipadas
// Tipando rotas REST
type Recurso = "users" | "posts" | "comments";
type Metodo = "GET" | "POST" | "PUT" | "DELETE";
type RotaBase = `/api/v1/${Recurso}`;
// "/api/v1/users" | "/api/v1/posts" | "/api/v1/comments"
type RotaComId = `/api/v1/${Recurso}/${number}`;
// "/api/v1/users/${number}" | ...
// Extraindo partes de uma rota com infer
type ExtrairRecurso<T> = T extends `/api/v1/${infer R}/${number}`
? R
: T extends `/api/v1/${infer R}`
? R
: never;
type R1 = ExtrairRecurso<"/api/v1/users">; // "users"
type R2 = ExtrairRecurso<"/api/v1/posts/42">; // "posts"
Percebe o poder? Você define as partes (recursos, métodos, campos) e o TypeScript gera todas as combinações válidas. Typos viram erros de compilação. Rotas inexistentes não passam no type check. É autocomplete completo sem runtime overhead.
Erros Comuns com Template Literal Types
Armadilhas que travam sua tipagem de strings
Explosão combinatória: se você combina duas unions de 10 elementos cada, o TypeScript gera 100 tipos. Três unions de 10 geram 1000. O compilador fica lento. Mantenha as unions pequenas ou use string com validação runtime.
Confundir tipo com valor: ${"get"}Name é um tipo literal, não uma string JavaScript. Não dá pra usar template literal types pra gerar strings em runtime. Eles existem só no sistema de tipos.
Esquecer o string & K em mapped types: quando usa K in keyof T dentro de um template, o K pode ser string | number | symbol. Faça string & K pra garantir que só strings entrem no template.
Capitalize não funciona com variáveis: Capitalize
Não usar as const em objetos: se você quer que o TypeScript infira tipos literais das strings de um objeto, use as const na declaração. Sem isso, as strings viram string genérico e o template literal perde a precisão.
Checklist de Template Literal Types
- Template literals usados pra gerar combinações de union types
- Utility types de string aplicados (Uppercase, Lowercase, Capitalize, Uncapitalize)
- Chaves de objeto geradas dinamicamente com mapped types + template
- Padrões de evento tipados com nomes gerados automaticamente
- Unions mantidas pequenas pra evitar explosão combinatória
- as const usado em objetos que alimentam os template types
- string & K usado em mapped types pra filtrar symbol e number
- Rotas e endpoints de API tipados com template literals
TypeScript Avançado na Prática
Template literal types são a cola que conecta strings dinâmicas ao sistema de tipos estático. Mas num projeto de produção, eles brilham quando combinados com mapped types, infer e generics pra criar APIs internas que se documentam sozinhas. No CrazyStack, você usa essas técnicas pra construir um SaaS completo com Node.js, React e TypeScript.
Se você quer criar abstrações que geram tipos automaticamente e fazem o compilador trabalhar a seu favor, esse é o caminho certo.
Perguntas frequentes
O Que São Template Literal Types
Template literal types usam a mesma sintaxe de template strings do JavaScript, mas no nível dos tipos. Em vez de montar strings em runtime, você monta tipos de string em tempo de compilação. A sintaxe é `${Type}` dentro de um tipo. Quando você escreve type Saudacao = `Olá, ${string}`, o TypeScript aceita qualquer string que comece com 'Olá, '. Se escrever `${"get" | "set"}${string}`, aceita qualquer string que comece com get ou set. O compilador verifica isso antes do código executar. A mágica aparece quando você combina template literals com union types. Se você tem type Cor = 'red' | 'blue' e type Tamanho = 'sm' | 'lg', o tipo `${Cor}-${Tamanho}` gera automaticamente 'red-sm' | 'red-lg' | 'blue-sm' | 'blue-lg'. O TypeScript faz o produto cartesiano sozinho.
Como Usar Template Literal Types Passo a Passo
Vamos construir do mais simples ao mais sofisticado. Cada passo desbloqueia um padrão novo.