<h2>Union Types: Combinando Múltiplos Tipos</h2>
<p>Union types permitem que uma variável aceite múltiplos tipos específicos. É um dos mecanismos mais práticos para criar APIs flexíveis sem perder segurança de tipos. Use a sintaxe de barra vertical ( | ) para declarar um union type.</p> <pre><code class="language-typescript">type Status = "pending" | "approved" | "rejected"; type Result = string | number;
function processPayment(status: Status): void {
if (status === "pending") {
console.log("Processando pagamento...");
}
}
const userId: Result = 42; // válido
const userName: Result = "João"; // válido
// const invalid: Result = true; // erro</code></pre>
<p>A vantagem emerge ao usar <strong>type guards</strong> para estreitar o tipo em tempo de execução. O TypeScript então oferece autocompletar específico para cada tipo. Padrões como <code>typeof</code>, <code>instanceof</code> e discriminated unions (usando propriedades literais) garantem código robusto e legível.</p>
<pre><code class="language-typescript">type Response = { success: true; data: string } | { success: false; error: string };
function handleResponse(res: Response): void {
if (res.success) {
console.log(res.data); // tipo: string
} else {
console.log(res.error); // tipo: string
}
}</code></pre>
<h2>Intersection Types: Combinando Propriedades</h2>
<p>Intersection types (& operador) mesclam múltiplos tipos em um único tipo que possui todas as propriedades de ambos. É diferente de union: aqui o valor deve satisfazer <strong>todos</strong> os tipos simultaneamente, não apenas um.</p>
<pre><code class="language-typescript">interface Funcionario {
nome: string;
salario: number;
}
interface Gerente {
departamento: string;
equipe: number;
}
type GerenteFuncionario = Funcionario & Gerente;
const gerente: GerenteFuncionario = {
nome: "Maria",
salario: 5000,
departamento: "TI",
equipe: 5
};</code></pre>
<p>Intersection types são especialmente úteis para composição de tipos e para adicionar capacidades específicas a tipos existentes. Use-os quando precisar garantir que um objeto cumpra múltiplos contratos simultaneamente. Evite uso excessivo, pois tornam assinaturas complexas; prefira composição com interfaces quando apropriado.</p>
<pre><code class="language-typescript">type Auditavel = { criado: Date; atualizado: Date };
type Validavel = { validar(): boolean };
type Documento = { titulo: string } & Auditavel & Validavel;
const doc: Documento = {
titulo: "Contrato",
criado: new Date(),
atualizado: new Date(),
validar() { return true; }
};</code></pre>
<h2>Generics: Reutilização Parametrizada de Tipos</h2>
<p>Generics são variáveis de tipo que tornam componentes reutilizáveis enquanto mantêm segurança estática. São fundamentais em TypeScript profissional. Declare um generic com <T> entre chevrons, onde T é convenção para "Type".</p>
<pre><code class="language-typescript">function obterPrimeiro<T>(lista: T[]): T {
return lista[0];
}
const numeros = obterPrimeiro([1, 2, 3]); // tipo: number
const nomes = obterPrimeiro(["Ana", "Bruno"]); // tipo: string
// Generics com constraints
function obterPropriedade<T, K extends keyof T>(obj: T, chave: K): T[K] {
return obj[chave];
}
const pessoa = { nome: "João", idade: 30 };
const idade = obterPropriedade(pessoa, "idade"); // tipo: number
// obterPropriedade(pessoa, "email"); // erro: propriedade inexistente</code></pre>
<p>Generics com constraints (<code>extends</code>) limitam quais tipos podem ser passados. Use <code>keyof T</code> para tipos seguros de propriedades. Para casos avançados, combine com unions e intersections. Generics em classes modelam estruturas genéricas como pilhas, filas e repositórios sem duplicação de código.</p>
<pre><code class="language-typescript">class Repositorio<T> {
private dados: T[] = [];
adicionar(item: T): void {
this.dados.push(item);
}
obter(indice: number): T | undefined {
return this.dados[indice];
}
listar(): T[] {
return [...this.dados];
}
}
interface Usuario {
id: number;
email: string;
}
const repoUsuarios = new Repositorio<Usuario>();
repoUsuarios.adicionar({ id: 1, email: "user@example.com" });
const usuario = repoUsuarios.obter(0); // tipo: Usuario | undefined</code></pre>
<h2>Utility Types: Transformações Prontas</h2>
<p>TypeScript fornece utility types nativos que transformam tipos existentes de forma poderosa. São verdadeiros multiplicadores de produtividade.</p>
<h3>Principais Utility Types</h3>
<p><strong>Partial<T></strong> torna todas as propriedades opcionais; <strong>Required<T></strong> faz o oposto. <strong>Pick<T, Keys></strong> seleciona propriedades específicas; <strong>Omit<T, Keys></strong> remove propriedades. <strong>Record<K, V></strong> cria objetos com chaves conhecidas. <strong>Readonly<T></strong> torna propriedades imutáveis.</p>
<pre><code class="language-typescript">interface Produto {
id: number;
nome: string;
preco: number;
descricao: string;
}
// Partial: todas propriedades opcionais para atualização
type AtualizarProduto = Partial<Produto>;
// Pick: apenas nome e preco para exibição
type ProdutoResumido = Pick<Produto, "nome" | "preco">;
// Omit: tudo exceto id (para criação)
type CriarProduto = Omit<Produto, "id">;
// Record: mapa de categorias
type CategoriaInventario = Record<"eletronicos" | "livros" | "roupas", number>;
const estoque: CategoriaInventario = {
eletronicos: 50,
livros: 120,
roupas: 200
};
// Readonly: preço não pode ser modificado
type ProdutoImutavel = Readonly<Produto>;</code></pre>
<p><strong>Extract<T, U></strong> extrai tipos que correspondem a U de T; <strong>Exclude<T, U></strong> faz o oposto. <strong>ReturnType<F></strong> obtém o tipo de retorno de uma função. <strong>Parameters<F></strong> extrai os tipos de parâmetros. Estes são indispensáveis em código metaprogramado e em bibliotecas robustas.</p>
<pre><code class="language-typescript">// Extract e Exclude
type Status = "ativo" | "inativo" | "pendente" | "cancelado"; type StatusAtivos = Exclude<Status, "cancelado">; // "ativo" | "inativo" | "pendente"
type ApenasAtivo = Extract<Status, "ativo">; // "ativo"
// ReturnType e Parameters
function autenticar(usuario: string, senha: string): { token: string } {
return { token: "xyz" };
}
type RetornoAuth = ReturnType<typeof autenticar>; // { token: string }
type ParamsAuth = Parameters<typeof autenticar>; // [string, string]</code></pre>
<h2>Conclusão</h2>
<p>Três aprendizados principais consolidam domínio sobre tipos avançados em TypeScript. Primeiro, <strong>unions e intersections</strong> são ferramentas complementares: use unions para alternativas e intersections para composição. Segundo, <strong>generics</strong> são essenciais para código reutilizável que mantém segurança de tipos; não tenha medo de combiná-los com constraints. Terceiro, <strong>utility types</strong> são multiplicadores de produtividade que evitam duplicação; domine os comuns (Partial, Pick, Omit, Record) e explore avançados conforme necessário. Aplicar estes conceitos transforma código em sistemas mais seguros, inteligíveis e mantíveis.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/types-from-types.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Advanced Types</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/utility-types.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Utility Types</a></li>
<li><a href="https://effectivetypescript.com/" target="_blank" rel="noopener noreferrer">Effective TypeScript - Dan Vanderkam</a> (Livro recomendado)</li>
<li><a href="https://egghead.io/" target="_blank" rel="noopener noreferrer">Type-Driven Development with TypeScript - Egghead</a></li>
<li><a href="https://basarat.gitbook.io/typescript/" target="_blank" rel="noopener noreferrer">TypeScript Deep Dive - Basarat Ali Syed</a></li>
</ul>