Template literal types avançados
1. Introdução aos Template Literal Types
Template literal types são uma poderosa feature do TypeScript que permite manipular tipos de strings em tempo de compilação. Diferente dos template literals do JavaScript, que operam em valores em tempo de execução, os template literal types trabalham exclusivamente no sistema de tipos.
// Sintaxe básica
type Saudacao = `Olá, ${string}`;
type Mensagem = `Bem-vindo, ${string}!`;
// Concatenação de tipos literais
type Nome = "João";
type SaudacaoPersonalizada = `Olá, ${Nome}`; // "Olá, João"
// Combinando com union types
type Prefixo = "user" | "admin";
type ID = `id_${number}`;
type Identificador = `${Prefixo}_${ID}`;
// "user_id_${number}" | "admin_id_${number}"
A principal diferença é que enquanto JavaScript avalia expressões em strings, TypeScript avalia tipos em tempo de compilação, permitindo criar validações e transformações complexas.
2. Inferência e Pattern Matching com infer
O operador infer dentro de template literal types permite extrair partes específicas de strings de tipos, funcionando como um pattern matching avançado.
// Extraindo IDs de strings padronizadas
type ExtrairId<T> = T extends `user_${infer Id}` ? Id : never;
type Teste1 = ExtrairId<"user_123">; // "123"
type Teste2 = ExtrairId<"admin_456">; // never
// Parsing de rotas
type RotaAPI<T> = T extends `/api/v${infer Versao}/${infer Recurso}`
? { versao: Versao; recurso: Recurso }
: never;
type Rota1 = RotaAPI<"/api/v2/usuarios">; // { versao: "2"; recurso: "usuarios" }
Este padrão é extremamente útil para tipar sistemas de roteamento, parsers de log e qualquer formato de string estruturado.
3. Manipulação de Casos (CamelCase, Snake_case, Kebab-case)
Com recursão e template literals, podemos criar utilitários de transformação de strings no sistema de tipos.
type CamelToSnake<S extends string> =
S extends `${infer First}${infer Rest}`
? First extends Capitalize<First>
? `_${Lowercase<First>}${CamelToSnake<Rest>}`
: `${First}${CamelToSnake<Rest>}`
: S;
type SnakeToCamel<S extends string> =
S extends `${infer First}_${infer Rest}`
? `${First}${Capitalize<SnakeToCamel<Rest>>}`
: S;
// Exemplos de uso
type Exemplo1 = CamelToSnake<"meuNomeComposto">; // "meu_nome_composto"
type Exemplo2 = SnakeToCamel<"meu_nome_composto">; // "meuNomeComposto"
A recursão percorre caractere por caractere, identificando maiúsculas e aplicando as transformações necessárias.
4. Validação e Restrição de Padrões com Union Types
Combinando template literals com union types, podemos criar padrões restritos que validam strings em tempo de compilação.
// Eventos com prefixo obrigatório
type Evento = `on${Capitalize<string>}`;
type EventoValido = Evento & ("onClick" | "onSubmit" | "onChange");
// Validação de paths de API
type MetodoHTTP = "GET" | "POST" | "PUT" | "DELETE";
type PathAPI = `/api/${string}/${string}`;
type RotaValida = `${MetodoHTTP} ${PathAPI}`;
type Rota1: RotaValida = "GET /api/usuarios/listar"; // OK
// type Rota2: RotaValida = "DELETE /api/usuarios"; // Erro: não corresponde ao padrão
// Nomes de métodos com prefixo
type Metodo = `get${Capitalize<string>}` | `set${Capitalize<string>}`;
type MetodoGetter = Metodo & `get${string}`;
class Usuario {
getNome(): string { return ""; }
setNome(valor: string): void {}
private processar(): void {} // Não corresponde ao padrão
}
5. Tipos Avançados com Múltiplos Placeholders e Recursão
Template literals podem conter múltiplos ${infer} e trabalhar com recursão para processamento complexo.
// Split recursivo
type Split<S extends string, Sep extends string> =
S extends `${infer Part}${Sep}${infer Rest}`
? [Part, ...Split<Rest, Sep>]
: S extends ""
? []
: [S];
type Partes = Split<"a,b,c,d", ",">; // ["a", "b", "c", "d"]
// Parsing de CSS-like strings
type PropriedadeCSS = `${infer Propriedade}:${infer Valor}`;
type ExtrairCSS<T> =
T extends `${infer Propriedade}:${infer Valor};`
? { [K in Propriedade]: Valor }
: never;
type CSS1 = ExtrairCSS<"color:red;">; // { color: "red" }
// Parsing de query strings
type QueryString = `${infer Chave}=${infer Valor}&${infer Rest}`;
type ParseQuery<T> =
T extends `${infer Chave}=${infer Valor}`
? { [K in Chave]: Valor }
: T extends `${infer Chave}=${infer Valor}&${infer Rest}`
? { [K in Chave]: Valor } & ParseQuery<Rest>
: {};
type Query = ParseQuery<"nome=João&idade=30">;
// { nome: "João"; idade: "30" }
6. Integração com Key Remapping e Mapped Types
Template literals são particularmente poderosos quando combinados com key remapping em mapped types.
// Renomeando chaves com prefixo
type RenameKeys<T> = {
[K in keyof T as `prefix_${K & string}`]: T[K]
};
type Usuario = { nome: string; idade: number };
type UsuarioPrefixo = RenameKeys<Usuario>;
// { prefix_nome: string; prefix_idade: number }
// Criando tipos de eventos automaticamente
type Eventos = "click" | "submit" | "change";
type Handlers = {
[K in Eventos as `on${Capitalize<K>}`]: () => void
};
// { onClick: () => void; onSubmit: () => void; onChange: () => void }
// Actions com nomes dinâmicos
type Actions<T extends string> = {
[K in T as `${K}Action`]: { type: K; payload: any }
};
type UserActions = Actions<"create" | "update" | "delete">;
// { createAction: { type: "create"; payload: any }; ... }
7. Casos de Uso Avançados em WebSockets e APIs
Template literals são ideais para tipar protocolos de comunicação e APIs REST.
// Tipando mensagens de WebSocket
type MensagemWS =
| `action:${string}`
| `data:${string}`
| `error:${string}`;
type ExtrairMensagem<T> =
T extends `action:${infer Action}`
? { tipo: "action"; acao: Action }
: T extends `data:${infer Data}`
? { tipo: "data"; dados: Data }
: T extends `error:${infer Erro}`
? { tipo: "error"; erro: Erro }
: never;
// Parsing de rotas REST dinâmicas
type RotaREST =
| `/users/${infer UserId}`
| `/users/${infer UserId}/posts/${infer PostId}`
| `/users/${infer UserId}/posts/${infer PostId}/comments/${infer CommentId}`;
type ExtrairParams<T> =
T extends `/users/${infer UserId}`
? { userId: UserId }
: T extends `/users/${infer UserId}/posts/${infer PostId}`
? { userId: UserId; postId: PostId }
: T extends `/users/${infer UserId}/posts/${infer PostId}/comments/${infer CommentId}`
? { userId: UserId; postId: PostId; commentId: CommentId }
: never;
type Params = ExtrairParams<"/users/123/posts/456">;
// { userId: "123"; postId: "456" }
8. Limitações, Performance e Boas Práticas
Apesar do poder, template literal types têm limitações importantes:
// Limitação: recursão profunda
type RecursaoProfunda<S extends string> =
S extends `${infer _}${infer Rest}`
? RecursaoProfunda<Rest> // Pode causar erro de recursão
: S;
// Boa prática: usar tipos utilitários built-in
type NomeMaiusculo = Capitalize<"joão">; // "João"
type NomeMinusculo = Uncapitalize<"João">; // "joão"
// Evitar infer excessivo em strings longas
type StringLonga = "a".repeat(50); // Pode causar lentidão no compilador
// Preferir validações simples quando possível
type ValidarEmail<T extends string> =
T extends `${string}@${string}.${string}` ? T : never;
type Email = ValidarEmail<"user@example.com">; // "user@example.com"
Boas práticas recomendadas:
1. Documente padrões complexos com comentários
2. Prefira tipos utilitários built-in (Capitalize, Uncapitalize, Uppercase, Lowercase)
3. Evite recursão profunda (mais de 50 níveis)
4. Use validações incrementais para melhor performance
5. Teste tipos complexos com casos de borda
Referências
- TypeScript Handbook: Template Literal Types — Documentação oficial completa sobre template literal types, incluindo sintaxe e exemplos avançados
- TypeScript Deep Dive: Template Literal Types — Guia detalhado com exemplos práticos e casos de uso do mundo real
- Type Challenges: Template Literal Types — Repositório com desafios práticos de TypeScript, incluindo exercícios sobre template literals
- Using Template Literal Types in TypeScript — Tutorial interativo com exemplos progressivos de template literal types
- Advanced TypeScript Patterns with Template Literals — Artigo técnico abordando padrões avançados como parsing de rotas e validação de strings
- TypeScript 4.1: Template Literal Types — Notas de lançamento do TypeScript 4.1 que introduziu template literal types