<h2>Union Types: Flexibilidade com Segurança</h2>
<p>Union Types permitem que uma variável ou parâmetro aceite múltiplos tipos diferentes. Em vez de forçar um único tipo, você declara que um valor pode ser de tipo A <strong>ou</strong> tipo B <strong>ou</strong> tipo C. Isso é fundamental quando você precisa trabalhar com dados que podem variar em sua natureza, mas ainda quer manter a segurança de tipos que TypeScript oferece.</p>
<p>A sintaxe é simples: use o operador pipe (<code> | </code>) entre os tipos. O compilador TypeScript garantirá que você só acesse propriedades e métodos comuns a todos os tipos na union, ou então execute verificações de tipo antes de usar funcionalidades específicas. Sem essa segurança, você poderia tentar chamar um método que existe apenas em um dos tipos, causando um erro em tempo de execução.</p> <pre><code class="language-typescript">type StatusResponse = 'sucesso' | 'erro' | 'pendente';
function procesarStatus(status: StatusResponse): void {
if (status === 'sucesso') {
console.log('Operação completada com sucesso');
} else if (status === 'erro') {
console.log('Ocorreu um erro durante a operação');
} else {
console.log('Ainda processando...');
}
}
procesarStatus('sucesso'); // ✓ Válido
procesarStatus('invalido'); // ✗ Erro de compilação</code></pre>
<p>Unions também funcionam com objetos complexos. Imagine um sistema que retorna dados diferentes baseado no tipo de requisição:</p>
<pre><code class="language-typescript">interface Usuario {
tipo: 'usuario';
nome: string;
email: string;
}
interface Administrador {
tipo: 'admin';
nome: string;
email: string;
permissoes: string[];
}
type Pessoa = Usuario | Administrador;
function exibirPessoa(pessoa: Pessoa): void {
console.log(Nome: ${pessoa.nome});
console.log(Email: ${pessoa.email});
// Propriedade 'permissoes' não existe em todas as unions
// console.log(pessoa.permissoes); // ✗ Erro
}</code></pre>
<h2>Intersection Types: Combinação de Características</h2>
<p>Intersection Types fazem o oposto das unions: em vez de "ou", temos "e". Um tipo intersection combina múltiplos tipos em um único tipo que possui todas as propriedades e métodos de cada um dos tipos combinados. Use o operador <code>&</code> para declarar intersections.</p>
<p>Isso é útil quando você precisa que um objeto cumpra contratos múltiplos simultaneamente. Por exemplo, um usuário que deve ser tanto uma Pessoa quanto um Funcionário. Em vez de duplicar código, você cria uma intersection que herda características de ambas as interfaces.</p>
<pre><code class="language-typescript">interface Pessoa {
nome: string;
idade: number;
}
interface Funcionario {
matricula: string;
salario: number;
departamento: string;
}
type PessoaFuncionario = Pessoa & Funcionario;
const joao: PessoaFuncionario = {
nome: 'João Silva',
idade: 35,
matricula: 'EMP001',
salario: 5000,
departamento: 'TI'
};
console.log(${joao.nome} trabalha em ${joao.departamento});</code></pre>
<p>Intersections são particularmente poderosas ao trabalhar com mixins e composição. Considere um cenário onde você precisa estender funcionalidade de uma classe base com comportamentos adicionais:</p>
<pre><code class="language-typescript">interface Auditável {
criadoEm: Date;
atualizadoEm: Date;
criadoPor: string;
}
interface Deletável {
deletadoEm?: Date;
deletadoPor?: string;
}
interface Produto extends Auditável, Deletável {
id: number;
nome: string;
preco: number;
}
const produto: Produto = {
id: 1,
nome: 'Notebook',
preco: 3500,
criadoEm: new Date('2024-01-01'),
atualizadoEm: new Date('2024-01-15'),
criadoPor: 'admin',
deletadoEm: undefined,
deletadoPor: undefined
};</code></pre>
<h2>Type Guards: Garantindo Segurança em Runtime</h2>
<p>Type Guards são técnicas que permitem ao TypeScript (e ao seu código) identificar o tipo exato de uma variável em um determinado ponto. Quando você tem uma union de tipos, o compilador fica conservador: só permite operações que são seguras para <strong>todos</strong> os tipos da union. Type guards resolvem isso reduzindo o escopo de tipos possíveis através de verificações explícitas.</p>
<h3>Verificação com typeof</h3>
<p>O <code>typeof</code> operator é ideal para diferenciar tipos primitivos. Use-o quando sua union contém strings, números, booleans ou funções:</p>
<pre><code class="language-typescript">type Valor = string | number | boolean;
function procesarValor(valor: Valor): void {
if (typeof valor === 'string') {
console.log(String em maiúscula: ${valor.toUpperCase()});
} else if (typeof valor === 'number') {
console.log(Número dobrado: ${valor * 2});
} else if (typeof valor === 'boolean') {
console.log(Booleano invertido: ${!valor});
}
}
procesarValor('hello'); // String em maiúscula: HELLO
procesarValor(42); // Número dobrado: 84
procesarValor(true); // Booleano invertido: false</code></pre>
<h3>Verificação com instanceof</h3>
<p>Use <code>instanceof</code> para verificar se um valor é instância de uma classe. Isso é essencial ao trabalhar com classes específicas em uma union:</p>
<pre><code class="language-typescript">class Cachorro {
latir(): string {
return 'Au au!';
}
}
class Gato {
miar(): string {
return 'Miau!';
}
}
type Animal = Cachorro | Gato;
function fazerSom(animal: Animal): void {
if (animal instanceof Cachorro) {
console.log(animal.latir());
} else if (animal instanceof Gato) {
console.log(animal.miar());
}
}
fazerSom(new Cachorro()); // Au au!
fazerSom(new Gato()); // Miau!</code></pre>
<h3>Verificação com Propriedades Discriminantes</h3>
<p>Quando você tem interfaces com uma propriedade comum que diferencia seus tipos (chamada discriminante), use-a para type narrowing. Essa é a abordagem mais elegante e performática:</p>
<pre><code class="language-typescript">interface Sucesso {
tipo: 'sucesso';
dados: unknown;
mensagem: string;
}
interface Falha {
tipo: 'falha';
erro: Error;
codigo: number;
}
type Resposta = Sucesso | Falha;
function tratarResposta(resposta: Resposta): void {
if (resposta.tipo === 'sucesso') {
console.log(Sucesso: ${resposta.mensagem});
console.log(Dados: ${JSON.stringify(resposta.dados)});
} else {
console.log(Erro ${resposta.codigo}: ${resposta.erro.message});
}
}
const respostaBom: Resposta = {
tipo: 'sucesso',
dados: { id: 1, nome: 'Test' },
mensagem: 'Operação concluída'
};
tratarResposta(respostaBom);</code></pre>
<h3>Type Predicates: Type Guards Customizados</h3>
<p>Quando verificações simples não são suficientes, crie funções que retornam type predicates. A sintaxe <code>is</code> informa ao TypeScript que a função foi executada com sucesso, o tipo foi confirmado:</p>
<pre><code class="language-typescript">interface Configuracao {
host: string;
porta: number;
ssl?: boolean;
}
interface Credenciais {
usuario: string;
senha: string;
}
type ConfigOuCredenciais = Configuracao | Credenciais;
function ehConfiguracao(obj: ConfigOuCredenciais): obj is Configuracao {
return 'host' in obj && 'porta' in obj;
}
function ehCredenciais(obj: ConfigOuCredenciais): obj is Credenciais {
return 'usuario' in obj && 'senha' in obj;
}
function conectar(config: ConfigOuCredenciais): void {
if (ehConfiguracao(config)) {
console.log(Conectando em ${config.host}:${config.porta});
} else if (ehCredenciais(config)) {
console.log(Autenticando usuário: ${config.usuario});
}
}
const config: ConfigOuCredenciais = {
host: 'localhost',
porta: 3000,
ssl: true
};
conectar(config);</code></pre>
<h2>Combinando Union, Intersection e Type Guards em Casos Práticos</h2>
<p>A verdadeira maestria vem quando você combina essas três técnicas em um cenário real. Considere um sistema de processamento de eventos que precisa lidar com diferentes tipos de eventos, cada um com suas próprias propriedades:</p>
<pre><code class="language-typescript">// Definindo eventos diferentes como union
type Evento = EventoUsuario | EventoProduto | EventoPagamento;
interface EventoUsuario {
tipo: 'usuario';
acao: 'criacao' | 'atualizacao' | 'delecao';
usuarioId: number;
timestamp: Date;
}
interface EventoProduto {
tipo: 'produto';
acao: 'criacao' | 'atualizacao' | 'delecao';
produtoId: number;
estoque: number;
timestamp: Date;
}
interface EventoPagamento {
tipo: 'pagamento';
acao: 'processado' | 'recusado' | 'pendente';
valor: number;
moeda: string;
timestamp: Date;
}
// Type guard reutilizável
function ehEventoUsuario(evento: Evento): evento is EventoUsuario {
return evento.tipo === 'usuario';
}
// Processador que combina tudo
function processarEvento(evento: Evento): void {
// Verificação geral: propriedades comuns a todos
console.log(Evento processado em ${evento.timestamp});
console.log(Ação: ${evento.acao});
// Type narrowing com discriminante
if (ehEventoUsuario(evento)) {
console.log(Usuário ID: ${evento.usuarioId});
} else if (evento.tipo === 'produto') {
console.log(Produto ID: ${evento.produtoId});
console.log(Estoque: ${evento.estoque});
} else if (evento.tipo === 'pagamento') {
console.log(Valor: ${evento.valor} ${evento.moeda});
}
}
// Testando
const eventoUser: Evento = {
tipo: 'usuario',
acao: 'criacao',
usuarioId: 123,
timestamp: new Date()
};
processarEvento(eventoUser);</code></pre>
<h2>Conclusão</h2>
<p>Você aprendeu que <strong>Union Types</strong> permitem flexibilidade ao aceitar múltiplos tipos, mantendo segurança através de verificações explícitas. <strong>Intersection Types</strong> combinam características de múltiplas interfaces, permitindo composição elegante de comportamentos. <strong>Type Guards</strong> reduzem o escopo de tipos através de verificações de runtime, usando técnicas como <code>typeof</code>, <code>instanceof</code>, propriedades discriminantes e type predicates customizados. Quando combinadas, essas três abordagens criam código TypeScript robusto, legível e imune a erros de tipo em tempo de execução.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://www.typescriptlang.org/docs/handbook/unions-and-intersections.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Union Types</a></li>
<li><a href="https://www.typescriptlang.org/docs/handbook/2/narrowing.html" target="_blank" rel="noopener noreferrer">TypeScript Handbook - Type Guards and Differentiating Types</a></li>
<li><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/instanceof" target="_blank" rel="noopener noreferrer">MDN Web Docs - instanceof operator</a></li>
<li><a href="https://basarat.gitbook.io/typescript/type-system/discriminated-unions" target="_blank" rel="noopener noreferrer">TypeScript Deep Dive - Discriminated Unions</a></li>
<li><a href="https://www.oreilly.com/library/view/programming-typescript/9781492037644/" target="_blank" rel="noopener noreferrer">O'Reilly - Programming TypeScript by Boris Cherny</a></li>
</ul>
<p><!-- FIM --></p>