JavaScript Avançado

O que Todo Dev Deve Saber sobre Template Literal Types e Recursive Types em TypeScript

6 min de leitura

O que Todo Dev Deve Saber sobre Template Literal Types e Recursive Types em TypeScript

Template Literal Types em TypeScript Template Literal Types permitem criar tipos baseados em literais de string usando a sintaxe de template literals. Introduzidos no TypeScript 4.4, eles são fundamentais para sistemas de tipos mais expressivos e seguros. Essencialmente, você define um tipo usando a sintaxe ${} dentro de backticks, possibilitando composição dinâmica de tipos. O caso de uso mais comum é validar e criar tipos para strings com padrões específicos. Imagine um sistema de eventos onde você precisa garantir que event handlers sigam uma convenção de nomenclatura: on${Capitalize } Combinando Union Types Template Literal Types brilham ao combinar múltiplas unions, criando todas as combinações possíveis: /${HTTPMethod}/${Endpoint} Type Inference e Extração Você pode extrair partes de uma string usando em tipos condicionais: /${infer M}/${infer E} Recursive Types: Estruturando Dados Infinitos Recursive Types permitem que um tipo se referencie, essencial para estruturas de árvore, grafos ou padrões aninhados. Ao contrário da recursão infinita, o TypeScript "expande" o tipo apenas quando necessário, mantendo

<h2>Template Literal Types em TypeScript</h2>

<p>Template Literal Types permitem criar tipos baseados em literais de string usando a sintaxe de template literals. Introduzidos no TypeScript 4.4, eles são fundamentais para sistemas de tipos mais expressivos e seguros. Essencialmente, você define um tipo usando a sintaxe ${} dentro de backticks, possibilitando composição dinâmica de tipos.</p>

<p>O caso de uso mais comum é validar e criar tipos para strings com padrões específicos. Imagine um sistema de eventos onde você precisa garantir que event handlers sigam uma convenção de nomenclatura:</p>

<pre><code class="language-typescript">type EventType = &quot;click&quot; | &quot;hover&quot; | &quot;focus&quot;;

type EventHandler = on${Capitalize&lt;EventType&gt;};

// Resultado: &quot;onClick&quot; | &quot;onHover&quot; | &quot;onFocus&quot;

const handler: EventHandler = &quot;onClick&quot;; // ✓ válido

const invalid: EventHandler = &quot;onchange&quot;; // ✗ erro</code></pre>

<h3>Combinando Union Types</h3>

<p>Template Literal Types brilham ao combinar múltiplas unions, criando todas as combinações possíveis:</p>

<pre><code class="language-typescript">type HTTPMethod = &quot;GET&quot; | &quot;POST&quot; | &quot;PUT&quot; | &quot;DELETE&quot;; type Endpoint = &quot;users&quot; | &quot;posts&quot; | &quot;comments&quot;;

type APIRoute = /${HTTPMethod}/${Endpoint};

// Gera 12 combinações automaticamente

const route1: APIRoute = &quot;/GET/users&quot;; // ✓

const route2: APIRoute = &quot;/POST/posts&quot;; // ✓

const route3: APIRoute = &quot;/PATCH/users&quot;; // ✗ erro</code></pre>

<h3>Type Inference e Extração</h3>

<p>Você pode extrair partes de uma string usando <code>infer</code> em tipos condicionais:</p>

<pre><code class="language-typescript">type ExtractMethod&lt;T&gt; =

T extends /${infer M}/${infer E} ? M : never;

type Method = ExtractMethod&lt;&quot;/GET/users&quot;&gt;; // &quot;GET&quot;</code></pre>

<h2>Recursive Types: Estruturando Dados Infinitos</h2>

<p>Recursive Types permitem que um tipo se referencie, essencial para estruturas de árvore, grafos ou padrões aninhados. Ao contrário da recursão infinita, o TypeScript &quot;expande&quot; o tipo apenas quando necessário, mantendo performance.</p>

<p>A aplicação clássica é criar tipos para estruturas aninhadas arbitrárias. Considere um sistema de validação de objetos profundamente aninhados:</p>

<pre><code class="language-typescript">type DeepPartial&lt;T&gt; = T extends object

? { [K in keyof T]?: DeepPartial&lt;T[K]&gt; }

: T;

interface User {

name: string;

profile: {

bio: string;

settings: {

notifications: boolean;

}

}

}

const partial: DeepPartial&lt;User&gt; = {

profile: {

settings: {} // válido: qualquer nível pode ser omitido

}

};</code></pre>

<h3>Recursive Types para Estruturas Heterogêneas</h3>

<p>Sistemas que processam JSONs complexos se beneficiam enormemente. Veja uma implementação de tipo para qualquer JSON válido:</p>

<pre><code class="language-typescript">type JSON =

string | number | boolean | null | JSON[] | { [key: string]: JSON };

const validJSON: JSON = {

user: {

name: &quot;Ana&quot;,

scores: [10, 20, { nested: true }],

active: null

}

}; // ✓ totalmente type-safe</code></pre>

<h3>Limiting Recursion com Profundidade Máxima</h3>

<p>Para evitar problemas de performance em certos cenários, você pode limitar a profundidade de recursão:</p>

<pre><code class="language-typescript">type DeepReadonly&lt;T, Depth extends number = 5&gt; =

Depth extends 0

? T

: T extends object

? { readonly [K in keyof T]: DeepReadonly&lt;T[K], [-1, 0, 1, 2, 3, 4][Depth]&gt; }

: T;</code></pre>

<h2>Combinando Template Literals com Tipos Recursivos</h2>

<p>A fusão de ambas as técnicas cria sistemas de tipos incrivelmente poderosos. Um exemplo prático: validar objetos com chaves dinâmicas baseadas em um padrão:</p>

<pre><code class="language-typescript">type DeepKeys&lt;T, Prefix = &quot;&quot;&gt; = T extends object

? {

[K in keyof T]: K extends string

? ${Prefix}${K} | DeepKeys&lt;T[K], ${Prefix}${K}.&gt;

: never

}[keyof T]

: never;

interface Config {

database: {

host: string;

port: number;

credentials: {

username: string;

password: string;

}

}

cache: {

enabled: boolean;

}

}

type ConfigPaths = DeepKeys&lt;Config&gt;;

// &quot;database&quot; | &quot;database.host&quot; | &quot;database.port&quot; // &quot;database.credentials&quot; | &quot;database.credentials.username&quot; | ...

function getConfig(key: ConfigPaths): string | number | boolean {

// implementação segura

return &quot;&quot;;

}

getConfig(&quot;database.credentials.username&quot;); // ✓

getConfig(&quot;database.invalid&quot;); // ✗ erro em tempo de compilação</code></pre>

<p>Esse padrão é usado em bibliotecas como Zod, tRPC e frameworks modernos para garantir type-safety em configurações complexas. A recursão navega por cada nível do objeto, enquanto template literals constroem strings do caminho dinamicamente.</p>

<h2>Conclusão</h2>

<p>Template Literal Types e Recursive Types são ferramentas avançadas que transformam o TypeScript de uma camada de validação em um <strong>poderoso sistema de constraints em tempo de compilação</strong>. Template Literals permitem criar tipos baseados em padrões de string com composição elegante, enquanto Recursive Types resolvem o desafio de representar estruturas aninhadas de profundidade arbitrária.</p>

<p>Dominar essas técnicas eleva significativamente a qualidade do seu código: erros são capturados antes de executar, autocompletar fica mais inteligente, e refatorações ficam seguras. Comece praticando com casos simples (validação de rotas, chaves de configuração) e evolua gradualmente para sistemas mais complexos.</p>

<h2>Referências</h2>

<ul>

<li><a href="https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook: Template Literal Types</a></li>

<li><a href="https://www.typescriptlang.org/docs/handbook/2/conditional-types.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook: Conditional Types</a></li>

<li><a href="https://www.typescriptlang.org/docs/handbook/advanced-types.html" target="_blank" rel="noopener noreferrer">Advanced TypeScript: Recursive Types</a></li>

<li><a href="https://www.totaltypescript.com/" target="_blank" rel="noopener noreferrer">Total TypeScript: Advanced Type Patterns</a></li>

<li><a href="https://www.oreilly.com/library/view/programming-typescript/9781492037644/" target="_blank" rel="noopener noreferrer">Programming TypeScript - Boris Cherny (O&#039;Reilly Media)</a></li>

</ul>

Comentários

Mais em JavaScript Avançado

Micro-frontends com React: Module Federation e Arquitetura Distribuída na Prática
Micro-frontends com React: Module Federation e Arquitetura Distribuída na Prática

O que são Micro-frontends e Module Federation Micro-frontends é uma arquitetu...

Dominando React Query Avançado: Cache, Mutations, Optimistic Updates e Prefetch em Projetos Reais
Dominando React Query Avançado: Cache, Mutations, Optimistic Updates e Prefetch em Projetos Reais

Dominando o Cache do React Query O cache é o coração do React Query. A biblio...

Como Usar Testes End-to-End com Playwright: Page Object Model e CI Integration em Produção
Como Usar Testes End-to-End com Playwright: Page Object Model e CI Integration em Produção

O que é Playwright e Por Que Page Object Model? Playwright é um framework de...