TypeScript vai muito além de simples anotações de tipo no JavaScript. Seu verdadeiro potencial está na composição de tipos, inferência automática e manipulação avançada, permitindo que você escreva código mais seguro, legível e eficiente. Neste artigo, exploramos práticas recomendadas que podem elevar suas habilidades e tornar seu código TypeScript mais poderoso.
1. Pense em "Conjuntos" ao Trabalhar com Tipos
Cada tipo em TypeScript pode ser visto como um conjunto de valores. Isso ajuda a entender operações como união (|) e interseção (&).
*Exemplo: *
type Dimensao = { altura: number; largura: number };
type Aparencia = { cor: string; opacidade: number };
type Janela = Dimensao & Aparencia; // { altura: number; largura: number; cor: string; opacidade: number }Aqui, & cria uma interseção, garantindo que Janela tenha todas as propriedades.
Comparação:
& Interseção: elementos comuns aos dois tipos
 `` União: elementos de ambos os tipos2. Diferencie "Tipo Declarado" e "Tipo Reduzido"
TypeScript ajusta automaticamente os tipos com base no fluxo do código.
Exemplo:
function processar(valor: string | boolean) {
  if (typeof valor === 'string') {
    console.log(valor.toUpperCase()); // OK, pois TypeScript reduz valor para string
  }
}Isso evita a necessidade de conversões desnecessárias.
3. Prefira União Discriminada a Campos Opcionais
Usar type com uma propriedade discriminatória melhora a segurança do código.
Errado:
type Veiculo = {
  tipo: 'carro' | 'moto';
  portas?: number;
  cilindradas?: number;
};Correto:
type Carro = { tipo: 'carro'; portas: number };
type Moto = { tipo: 'moto'; cilindradas: number };
type Veiculo = Carro | Moto;4. Use "Type Predicate" para Evitar "Type Assertion"
Usar um type predicate permite que o TypeScript compreenda automaticamente a especialização de tipos dentro de funções, evitando a necessidade de conversões explícitas (as). Isso melhora a segurança do código e reduz erros inesperados.
Exemplo:
function isCarro(veiculo: Veiculo): veiculo is Carro {
  return veiculo.tipo === 'carro';
}
const veiculos: Veiculo[] = obterVeiculos();
const carros = veiculos.filter(isCarro); // TypeScript agora sabe que isso é um Carro[]Outro Exemplo:
type Animal = { tipo: 'cachorro' | 'gato'; som?: string };
type Cachorro = { tipo: 'cachorro'; som: 'latido' };
type Gato = { tipo: 'gato'; som: 'miado' };
type Especie = Cachorro | Gato;
function isCachorro(animal: Especie): animal is Cachorro {
  return animal.tipo === 'cachorro';
}
const animais: Especie[] = [
  { tipo: 'cachorro', som: 'latido' },
  { tipo: 'gato', som: 'miado' }
];
const apenasCachorros = animais.filter(isCachorro);
console.log(apenasCachorros); // [{ tipo: 'cachorro', som: 'latido' }]5. Controle como Tipos de União São Distribuídos
TypeScript distribui tipos em uniões automaticamente. Podemos alterar isso com colchetes:
Exemplo:
type ToList = [T] extends [Array] ? T : T[];
type Resultado = ToList; // Agora é (string | number)[]6. Utilize "Exaustividade" em switch para Evitar Casos Não Tratados
Usar never pode ajudar a garantir que todas as possibilidades sejam cobertas:
function calcularDesconto(produto: Produto) {
  switch (produto.categoria) {
    case 'eletronico':
      return produto.preco * 0.9;
    case 'vestuario':
      return produto.preco * 0.8;
    default:
      const exaustivo: never = produto;
      throw new Error('Categoria desconhecida');
  }
}7. Prefira type a interface
Use type, a menos que precise de declaration merging ou de OO:
type Cliente = { nome: string; idade: number; email: string };8. Use Tuplas ao Invés de Arrays Genéricos
type Coordenada = [number, number, string];
const minhaLocalizacao: Coordenada = [40.7128, -74.0060, 'Nova York'];9. Controle a Especificidade da Inferência de Tipos
Use as const para restringir valores.
Use satisfies para validar sem alterar inferência:
const produto = { preco: 100, nome: 'Notebook' } satisfies { preco: number; nome?: string };
console.log(produto.nome.length); // Ok, pois TypeScript sabe que nome não é `undefined`**10. Evite Código Repetitivo com Manipulação de Tipos
Exemplo:
type Funcionario = { nome: string; cargo: string; salario: number };
type DadosSalariais = Pick;Isso elimina redundância ao criar novos tipos baseados em tipos existentes.
