TypeScript se tornou o padrão da indústria para desenvolvimento web em grande escala. Embora a maioria dos desenvolvedores conheça o básico de interfaces e tipos, o verdadeiro poder está no seu sistema de tipos avançado. Aqui estão 5 padrões que irão distingui-lo como engenheiro sênior.
1. Generic Constraints
Generics são poderosos, mas às vezes você precisa limitar o que pode ser passado. extends é seu aliado aqui.
interface HasId { id: string; } function getById<T extends HasId>(list: T[], id: string): T | undefined { return list.find((item) => item.id === id); }
Ao garantir que T estende HasId, garantimos que acessar .id dentro da função é seguro.
2. Conditional Types
Tipos condicionais permitem criar mapeamentos de tipos não uniformes. A sintaxe é semelhante ao operador ternário em JavaScript.
type IsString<T> = T extends string ? true : false; type A = IsString<string>; // true type B = IsString<number>; // false
Um caso de uso prático é filtrar tipos de uma union:
type Diff<T, U> = T extends U ? never : T; type NonNullable<T> = Diff<T, null | undefined>;
3. Mapped Types
Mapped types permitem criar novos tipos baseados em tipos existentes, transformando propriedades.
type ReadOnly<T> = { readonly [P in keyof T]: T[P]; }; interface User { name: string; age: number; } type ReadOnlyUser = ReadOnly<User>;
Você pode até adicionar ou remover modificadores:
type Mutable<T> = { -readonly [P in keyof T]: T[P]; };
4. Template Literal Types
Introduzidos no TypeScript 4.1, eles permitem manipular tipos de string diretamente.
type World = 'world'; type Greeting = `hello ${World}`; // "hello world" type Color = 'red' | 'blue'; type Quantity = 'light' | 'dark'; type Palette = `${Quantity}-${Color}`; // "light-red" | "light-blue" | "dark-red" | "dark-blue"
Isso é incrivelmente útil para tipar strings que seguem um padrão específico, como classes CSS ou nomes de eventos.
5. A Palavra-chave infer
A palavra-chave infer dentro de tipos condicionais permite extrair tipos de outros tipos.
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any; function check(): boolean { return true; } type CheckReturn = ReturnType<typeof check>; // boolean
Aqui, estamos pedindo ao TypeScript para "inferir" o tipo de retorno R de uma função e retorná-lo.
Conclusão
Dominar esses padrões permite escrever bibliotecas e utilitários que são robustos e proporcionam uma excelente experiência para o desenvolvedor (DX). O objetivo do TypeScript avançado não é complexidade pela complexidade, mas sim segurança e expressividade.