Pular para o conteúdo
TypeScript

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.

  1. 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.
  2. 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.
  3. Passo 3 - Use os utility types de string: Uppercase<'hello'> vira 'HELLO'. Capitalize<'name'> vira 'Name'. Use pra gerar variações padronizadas de nomes.
  4. Passo 4 - Gere nomes de getter/setter: type Getter = get${Capitalize}. Getter<'name'> vira 'getName'. Isso é padrão em frameworks e ORMs.
  5. Passo 5 - Monte padrões de evento: type EventHandler = on${Capitalize}Change. EventHandler<'name'> vira 'onNameChange'. Tipagem perfeita pra formulários e estados.
  6. 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 funciona com tipos literais ('hello' vira 'Hello'). Se T for string genérico, o resultado é string. Precisa de tipos literais concretos pra manipulação funcionar.

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.