<h2>Entendendo o Sistema de Tipos Avançado do TypeScript</h2>
<p>O TypeScript oferece um sistema de tipos extremamente poderoso que vai muito além de tipos simples como <code>string</code> e <code>number</code>. Quando você domina conceitos como <code>infer</code>, <code>extends</code> e <code>Mapped Types</code>, consegue criar abstrações sofisticadas que tornam seu código mais seguro, reutilizável e expressivo. Este artigo é uma jornada prática por esses três pilares fundamentais do TypeScript avançado.</p>
<p>Estes conceitos não são apenas acadêmicos — grandes frameworks como React, Vue e bibliotecas utilitárias dependem deles. Uma vez que você os compreender, verá oportunidades de melhorar drasticamente a qualidade do seu código TypeScript.</p>
<h2>Extends: O Fundamento das Restrições de Tipo</h2>
<p>O <code>extends</code> em TypeScript é um operador de restrição que permite você verificar se um tipo é compatível com outro. Diferente da herança em classes, aqui estamos no universo dos tipos, não de instâncias. Use <code>extends</code> para criar condicionais de tipo e limitar o que pode ser passado como genérico.</p>
<pre><code class="language-typescript">// Exemplo básico: restrição simples
function processar<T extends string | number>(valor: T): T {
return valor;
}
processar("texto"); // ✓ Funciona
processar(123); // ✓ Funciona
processar(true); // ✗ Erro: boolean não estende string | number
// Exemplo prático: validação de chaves de objeto
type User = { name: string; age: number; email: string };
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user: User = { name: "Ana", age: 28, email: "ana@email.com" };
const nome = getProperty(user, "name"); // ✓ "name" é chave válida
getProperty(user, "telefone"); // ✗ Erro: "telefone" não existe em User</code></pre>
<p>A real força do <code>extends</code> emerge quando combinado com types condicionais. Use a sintaxe <code>T extends U ? X : Y</code> para criar ramificações lógicas nos tipos. Isso permite que você escreva tipos que se comportam diferentemente dependendo de suas entradas.</p>
<pre><code class="language-typescript">// Tipo condicional prático
type IsString<T> = T extends string ? true : false;
type A = IsString<"olá">; // true
type B = IsString<number>; // false
// Exemplo real: extrair tipo de array
type ArrayElement<T> = T extends (infer E)[] ? E : never;
type NumArray = ArrayElement<number[]>; // number
type StrArray = ArrayElement<string[]>; // string
type NotArray = ArrayElement<boolean>; // never</code></pre>
<h2>Infer: Capturando Tipos Desconhecidos</h2>
<p>O <code>infer</code> é uma palavra-chave mágica que permite você <strong>extrair e capturar tipos</strong> de estruturas complexas sem conhecê-los antecipadamente. Ele só funciona dentro de um <code>extends</code> e marca um local onde TypeScript deve "adivinhar" qual é o tipo.</p>
<p>Imagine que você recebe um tipo complexo e precisa extrair um pedaço específico dele. É exatamente para isso que <code>infer</code> existe. A sintaxe é simples: quando TypeScript vê <code>infer T</code>, ele captura aquele tipo em uma variável <code>T</code> que você pode usar depois.</p>
<pre><code class="language-typescript">// Exemplo 1: Extrair tipo de Promise
type Awaited<T> = T extends Promise<infer U> ? U : T;
type ResultA = Awaited<Promise<string>>; // string
type ResultB = Awaited<number>; // number
// Exemplo 2: Extrair tipos de função
type FunctionReturn<T> = T extends (...args: any[]) => infer R ? R : never;
function saudar(nome: string): string {
return Olá, ${nome}!;
}
type SaudacaoReturn = FunctionReturn<typeof saudar>; // string
// Exemplo 3: Extrair parâmetros de função
type FunctionParams<T> = T extends (...args: infer P) => any ? P : never;
type SaudacaoParams = FunctionParams<typeof saudar>; // [nome: string]</code></pre>
<p>A verdadeira beleza do <code>infer</code> aparece quando você processa tipos recursivamente ou trabalha com estruturas aninhadas. É a ferramenta que permite criar utilitários tipo-seguros para bibliotecas e frameworks complexos.</p>
<h2>Mapped Types: Transformando Estruturas de Tipos</h2>
<p>Um <code>Mapped Type</code> é um tipo que itera sobre as propriedades de outro tipo e cria um novo tipo transformado. Use a sintaxe <code>{ [K in Keys]: T }</code> para mapear sobre chaves e gerar novas propriedades. Isso é especialmente útil para criar variações de tipos existentes.</p>
<pre><code class="language-typescript">// Exemplo 1: Tornar todas as propriedades opcionais
type Partial<T> = {
[K in keyof T]?: T[K];
};
type User = { name: string; age: number };
type OptionalUser = Partial<User>;
// Resultado: { name?: string; age?: number }
// Exemplo 2: Criar versão "readonly" de um tipo
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
type ImmutableUser = Readonly<User>;
// Resultado: { readonly name: string; readonly age: number }
// Exemplo 3: Converter todas as propriedades em getters
type Getters<T> = {
[K in keyof T as get${Capitalize<string & K>}]: () => T[K];
};
type UserGetters = Getters<User>;
// Resultado: { getName: () => string; getAge: () => number }</code></pre>
<p>Combine Mapped Types com tipos condicionais e <code>infer</code> para criar transformações sofisticadas. Isso é o que permite que bibliotecas como <code>ts-jest</code> e ferramentas de validação ofereçam suporte tipo-seguro tão impressionante.</p>
<pre><code class="language-typescript">// Exemplo avançado: Filtrar propriedades por tipo
type PropertiesOfType<T, U> = {
[K in keyof T as T[K] extends U ? K : never]: T[K];
};
type Product = { name: string; price: number; active: boolean };
type StringProps = PropertiesOfType<Product, string>; // { name: string }
type NumberProps = PropertiesOfType<Product, number>; // { price: number }</code></pre>
<h2>Conclusão</h2>
<p>Três aprendizados essenciais consolidam sua maestria em tipos condicionais avançados: Primeiro, <strong><code>extends</code> é seu validador</strong> — use-o para restringir genéricos e criar condicionais de tipo que bifurcam a lógica baseado em compatibilidade. Segundo, <strong><code>infer</code> é seu extrator</strong> — permite capturar tipos de estruturas complexas sem conhecê-los explicitamente, sendo indispensável para tipos como <code>Awaited</code> e <code>ReturnType</code>. Terceiro, <strong>Mapped Types são seus transformadores</strong> — iteram sobre tipos existentes e geram novos tipos derivados, mantendo segurança em larga escala.</p>
<p>Domine esses três conceitos em conjunto e você terá as ferramentas para construir sistemas de tipos robustos, reutilizáveis e que escalam com seu projeto.</p>
<h2>Referências</h2>
<ul>
<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/2/mapped-types.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Mapped Types</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/generics.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Generics</a></li>
<li><a href="https://www.totaltypescript.com/" target="_blank" rel="noopener noreferrer">Advanced TypeScript: Master Type System Concepts</a></li>
<li><a href="https://www.mattpocock.com/tutorials/typescript" target="_blank" rel="noopener noreferrer">Deep Dive into TypeScript Type System by Matt Pocock</a></li>
</ul>