JavaScript Avançado

Como Usar Tipos Condicionais em TypeScript: infer, extends e Mapped Types em Produção

8 min de leitura

Como Usar Tipos Condicionais em TypeScript: infer, extends e Mapped Types em Produção

Entendendo o Sistema de Tipos Avançado do TypeScript O TypeScript oferece um sistema de tipos extremamente poderoso que vai muito além de tipos simples como e . Quando você domina conceitos como , e , 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. 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. Extends: O Fundamento das Restrições de Tipo O 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 para criar condicionais de tipo e limitar o que pode ser passado como genérico. A real força do emerge quando combinado com types

<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&lt;T extends string | number&gt;(valor: T): T {

return valor;

}

processar(&quot;texto&quot;); // ✓ 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&lt;T, K extends keyof T&gt;(obj: T, key: K): T[K] {

return obj[key];

}

const user: User = { name: &quot;Ana&quot;, age: 28, email: &quot;ana@email.com&quot; };

const nome = getProperty(user, &quot;name&quot;); // ✓ &quot;name&quot; é chave válida

getProperty(user, &quot;telefone&quot;); // ✗ Erro: &quot;telefone&quot; 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&lt;T&gt; = T extends string ? true : false;

type A = IsString&lt;&quot;olá&quot;&gt;; // true

type B = IsString&lt;number&gt;; // false

// Exemplo real: extrair tipo de array

type ArrayElement&lt;T&gt; = T extends (infer E)[] ? E : never;

type NumArray = ArrayElement&lt;number[]&gt;; // number

type StrArray = ArrayElement&lt;string[]&gt;; // string

type NotArray = ArrayElement&lt;boolean&gt;; // 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 &quot;adivinhar&quot; 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&lt;T&gt; = T extends Promise&lt;infer U&gt; ? U : T;

type ResultA = Awaited&lt;Promise&lt;string&gt;&gt;; // string

type ResultB = Awaited&lt;number&gt;; // number

// Exemplo 2: Extrair tipos de função

type FunctionReturn&lt;T&gt; = T extends (...args: any[]) =&gt; infer R ? R : never;

function saudar(nome: string): string {

return Olá, ${nome}!;

}

type SaudacaoReturn = FunctionReturn&lt;typeof saudar&gt;; // string

// Exemplo 3: Extrair parâmetros de função

type FunctionParams&lt;T&gt; = T extends (...args: infer P) =&gt; any ? P : never;

type SaudacaoParams = FunctionParams&lt;typeof saudar&gt;; // [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&lt;T&gt; = {

[K in keyof T]?: T[K];

};

type User = { name: string; age: number };

type OptionalUser = Partial&lt;User&gt;;

// Resultado: { name?: string; age?: number }

// Exemplo 2: Criar versão &quot;readonly&quot; de um tipo

type Readonly&lt;T&gt; = {

readonly [K in keyof T]: T[K];

};

type ImmutableUser = Readonly&lt;User&gt;;

// Resultado: { readonly name: string; readonly age: number }

// Exemplo 3: Converter todas as propriedades em getters

type Getters&lt;T&gt; = {

[K in keyof T as get${Capitalize&lt;string &amp; K&gt;}]: () =&gt; T[K];

};

type UserGetters = Getters&lt;User&gt;;

// Resultado: { getName: () =&gt; string; getAge: () =&gt; 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&lt;T, U&gt; = {

[K in keyof T as T[K] extends U ? K : never]: T[K];

};

type Product = { name: string; price: number; active: boolean };

type StringProps = PropertiesOfType&lt;Product, string&gt;; // { name: string }

type NumberProps = PropertiesOfType&lt;Product, number&gt;; // { 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>

Comentários

Mais em JavaScript Avançado

Dominando Segurança, Auditoria de Dependências e Hardening Final em Node.js em Projetos Reais
Dominando Segurança, Auditoria de Dependências e Hardening Final em Node.js em Projetos Reais

Segurança em Node.js: Fundamentos e Práticas Essenciais A segurança em aplica...

Boas Práticas de PostgreSQL Avançado com Node.js: Transactions, CTEs e Window Functions para Times Ágeis
Boas Práticas de PostgreSQL Avançado com Node.js: Transactions, CTEs e Window Functions para Times Ágeis

Transactions com PostgreSQL e Node.js As transações são fundamentais para gar...

Boas Práticas de Hooks Customizados em React: Abstraindo Lógica Reutilizável para Times Ágeis
Boas Práticas de Hooks Customizados em React: Abstraindo Lógica Reutilizável para Times Ágeis

Entendendo Hooks Customizados Um Hook customizado é uma função JavaScript que...