Fala dev! 👋
TypeScript não é mais opcional no desenvolvimento frontend moderno. É uma ferramenta essencial que previne bugs, melhora a experiência de desenvolvimento e facilita a manutenção de código.
Neste guia avançado, vou te mostrar as técnicas mais poderosas do TypeScript que todo desenvolvedor frontend profissional precisa dominar.
🎯 Por que TypeScript avançado?
Benefícios reais:
- 🐛 90% menos bugs em produção
- 🧠 IntelliSense mais inteligente
- 🔄 Refactoring seguro e confiável
- 📚 Documentação viva do código
- 👥 Colaboração mais eficiente
- 🚀 Performance de desenvolvimento
Estatísticas impressionantes:
- 78% dos projetos React usam TypeScript
- 65% redução no tempo de debugging
- 40% melhoria na velocidade de desenvolvimento
🔧 Generics - O poder da flexibilidade
1. Generics básicos
// ❌ Função sem generics (limitada)
function getFirstItem(items: any[]): any {
return items[0]
}
// ✅ Com generics (flexível e type-safe)
function getFirstItem<T>(items: T[]): T | undefined {
return items[0]
}
// Uso
const numbers = [1, 2, 3, 4, 5]
const firstNumber = getFirstItem(numbers) // number | undefined
const names = ['João', 'Maria', 'Pedro']
const firstName = getFirstItem(names) // string | undefined2. Generics com constraints
// Constraint: T deve ter propriedade id
interface Identifiable {
id: string
}
function updateItem<T extends Identifiable>(
items: T[],
id: string,
updates: Partial<T>
): T[] {
return items.map(item =>
item.id === id ? { ...item, ...updates } : item
)
}
// Uso
interface User extends Identifiable {
name: string
email: string
}
const users: User[] = [
{ id: '1', name: 'João', email: 'joao@email.com' },
{ id: '2', name: 'Maria', email: 'maria@email.com' }
]
const updatedUsers = updateItem(users, '1', { name: 'João Silva' })3. Generics em React components
// Componente genérico para listas
interface ListProps<T> {
items: T[]
renderItem: (item: T, index: number) => React.ReactNode
keyExtractor: (item: T) => string
emptyMessage?: string
}
function List<T>({
items,
renderItem,
keyExtractor,
emptyMessage = 'Nenhum item encontrado'
}: ListProps<T>) {
if (items.length === 0) {
return <div className="text-gray-500">{emptyMessage}</div>
}
return (
<div className="space-y-2">
{items.map((item, index) => (
<div key={keyExtractor(item)}>
{renderItem(item, index)}
</div>
))}
</div>
)
}
// Uso com diferentes tipos
interface Product {
id: string
name: string
price: number
}
interface User {
id: string
name: string
email: string
}
function App() {
const products: Product[] = [
{ id: '1', name: 'Notebook', price: 2500 },
{ id: '2', name: 'Mouse', price: 50 }
]
const users: User[] = [
{ id: '1', name: 'João', email: 'joao@email.com' },
{ id: '2', name: 'Maria', email: 'maria@email.com' }
]
return (
<div>
<h2>Produtos</h2>
<List
items={products}
keyExtractor={(product) => product.id}
renderItem={(product) => (
<div className="border p-2 rounded">
<h3>{product.name}</h3>
<p>R$ {product.price}</p>
</div>
)}
/>
<h2>Usuários</h2>
<List
items={users}
keyExtractor={(user) => user.id}
renderItem={(user) => (
<div className="border p-2 rounded">
<h3>{user.name}</h3>
<p>{user.email}</p>
</div>
)}
/>
</div>
)
}🛠️ Utility Types - Ferramentas poderosas
1. Partial, Required, Pick, Omit
interface User {
id: string
name: string
email: string
age: number
isActive: boolean
}
// Partial - todas as propriedades opcionais
type UserUpdate = Partial<User>
// Equivale a: { id?: string; name?: string; email?: string; age?: number; isActive?: boolean }
// Required - todas as propriedades obrigatórias
type UserRequired = Required<User>
// Todas as propriedades são obrigatórias
// Pick - selecionar propriedades específicas
type UserBasicInfo = Pick<User, 'id' | 'name' | 'email'>
// { id: string; name: string; email: string }
// Omit - excluir propriedades específicas
type UserWithoutId = Omit<User, 'id'>
// { name: string; email: string; age: number; isActive: boolean }
// Uso prático
function updateUser(id: string, updates: UserUpdate) {
// Atualiza apenas as propriedades fornecidas
}
function createUser(userData: Omit<User, 'id'>) {
// Cria usuário sem ID (gerado automaticamente)
return {
id: crypto.randomUUID(),
...userData
}
}2. Record, Exclude, Extract
// Record - criar objeto com chaves e valores tipados
type Theme = 'light' | 'dark'
type ColorScheme = Record<Theme, string>
const colors: ColorScheme = {
light: '#ffffff',
dark: '#000000'
}
// Exclude - excluir tipos de uma união
type AllColors = 'red' | 'green' | 'blue' | 'yellow'
type PrimaryColors = Exclude<AllColors, 'yellow'>
// 'red' | 'green' | 'blue'
// Extract - extrair tipos de uma união
type Status = 'loading' | 'success' | 'error' | 'idle'
type LoadingStates = Extract<Status, 'loading' | 'idle'>
// 'loading' | 'idle'3. NonNullable, ReturnType, Parameters
// NonNullable - remover null e undefined
type MaybeString = string | null | undefined
type DefiniteString = NonNullable<MaybeString>
// string
// ReturnType - tipo de retorno de uma função
function getUser(id: string): Promise<User> {
return fetch(`/api/users/${id}`).then(res => res.json())
}
type UserPromise = ReturnType<typeof getUser>
// Promise<User>
// Parameters - tipos dos parâmetros de uma função
type GetUserParams = Parameters<typeof getUser>
// [string]
// Uso prático
async function handleGetUser(...args: Parameters<typeof getUser>) {
const [id] = args
console.log('Buscando usuário:', id)
return getUser(id)
}🔄 Conditional Types - Lógica de tipos
1. Conditional Types básicos
// Sintaxe: T extends U ? X : Y
type IsString<T> = T extends string ? true : false
type Test1 = IsString<string> // true
type Test2 = IsString<number> // false
// Exemplo prático
type ApiResponse<T> = T extends string
? { message: T }
: { data: T }
type StringResponse = ApiResponse<string> // { message: string }
type DataResponse = ApiResponse<User[]> // { data: User[] }2. Infer - inferir tipos
// Inferir tipo de retorno de uma função
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never
function getUser(): User { return {} as User }
function getUsers(): User[] { return [] }
type UserType = GetReturnType<typeof getUser> // User
type UsersType = GetReturnType<typeof getUsers> // User[]
// Inferir tipo de array
type ArrayElement<T> = T extends (infer U)[] ? U : never
type StringArray = string[]
type StringElement = ArrayElement<StringArray> // string
type NumberArray = number[]
type NumberElement = ArrayElement<NumberArray> // number3. Mapped Types
// Transformar todas as propriedades de um tipo
type Optional<T> = {
[K in keyof T]?: T[K]
}
type Readonly<T> = {
readonly [K in keyof T]: T[K]
}
// Exemplo prático
interface Product {
id: string
name: string
price: number
inStock: boolean
}
type OptionalProduct = Optional<Product>
// { id?: string; name?: string; price?: number; inStock?: boolean }
type ReadonlyProduct = Readonly<Product>
// { readonly id: string; readonly name: string; readonly price: number; readonly inStock: boolean }🎨 Template Literal Types
1. String manipulation
// Capitalizar primeira letra
type Capitalize<S extends string> = S extends `${infer F}${infer R}`
? `${Uppercase<F>}${R}`
: S
type CapitalizedName = Capitalize<'hello'> // 'Hello'
// Adicionar prefixo
type AddPrefix<T extends string, P extends string> = `${P}${T}`
type PrefixedId = AddPrefix<'user', 'id_'> // 'id_user'
// Exemplo prático
type EventName = 'click' | 'hover' | 'focus'
type EventHandler = `on${Capitalize<EventName>}`
// 'onClick' | 'onHover' | 'onFocus'2. API Routes com template literals
// Definir rotas da API
type ApiRoute = `/api/${string}`
type UserRoute = `/api/users/${string}`
type ProductRoute = `/api/products/${string}`
// Função genérica para fazer requests
async function apiRequest<T>(
url: ApiRoute,
options?: RequestInit
): Promise<T> {
const response = await fetch(url, options)
if (!response.ok) {
throw new Error(`API Error: ${response.status}`)
}
return response.json()
}
// Uso tipado
const user = await apiRequest<User>(`/api/users/${userId}`)
const products = await apiRequest<Product[]>(`/api/products`)🔧 Advanced Patterns
1. Discriminated Unions
// Definir estados de loading com discriminação
type LoadingState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: User[] }
| { status: 'error'; error: string }
// Função que usa discriminated union
function handleLoadingState(state: LoadingState) {
switch (state.status) {
case 'idle':
return 'Pronto para carregar'
case 'loading':
return 'Carregando...'
case 'success':
return `Carregados ${state.data.length} usuários` // TypeScript sabe que data existe
case 'error':
return `Erro: ${state.error}` // TypeScript sabe que error existe
}
}2. Branded Types
// Criar tipos únicos para evitar confusão
type UserId = string & { readonly __brand: 'UserId' }
type ProductId = string & { readonly __brand: 'ProductId' }
// Funções para criar branded types
function createUserId(id: string): UserId {
return id as UserId
}
function createProductId(id: string): ProductId {
return id as ProductId
}
// Uso - previne confusão entre IDs
function getUser(id: UserId): Promise<User> {
// Só aceita UserId, não ProductId
}
function getProduct(id: ProductId): Promise<Product> {
// Só aceita ProductId, não UserId
}
// Erro de compilação se misturar tipos
const userId = createUserId('123')
const productId = createProductId('456')
getUser(userId) // ✅ OK
getUser(productId) // ❌ Erro: Argument of type 'ProductId' is not assignable to parameter of type 'UserId'3. Function Overloads
// Sobrecarga de funções
function createElement(tag: 'div'): HTMLDivElement
function createElement(tag: 'span'): HTMLSpanElement
function createElement(tag: 'button'): HTMLButtonElement
function createElement(tag: string): HTMLElement {
return document.createElement(tag) as HTMLElement
}
// Uso com tipos específicos
const div = createElement('div') // HTMLDivElement
const span = createElement('span') // HTMLSpanElement
const button = createElement('button') // HTMLButtonElement🧪 Testing com TypeScript
1. Tipos para testes
// Mock types
type MockFunction<T extends (...args: any[]) => any> = jest.MockedFunction<T>
// Mock de API
interface ApiClient {
get<T>(url: string): Promise<T>
post<T>(url: string, data: any): Promise<T>
}
type MockApiClient = {
[K in keyof ApiClient]: MockFunction<ApiClient[K]>
}
// Test helper
function createMockApiClient(): MockApiClient {
return {
get: jest.fn(),
post: jest.fn()
}
}
// Uso nos testes
describe('UserService', () => {
let mockApi: MockApiClient
beforeEach(() => {
mockApi = createMockApiClient()
})
it('should fetch user', async () => {
const mockUser: User = { id: '1', name: 'João', email: 'joao@email.com' }
mockApi.get.mockResolvedValue(mockUser)
const user = await mockApi.get<User>('/users/1')
expect(user).toEqual(mockUser)
expect(mockApi.get).toHaveBeenCalledWith('/users/1')
})
})2. Test utilities
// Factory para criar dados de teste
type TestDataFactory<T> = (overrides?: Partial<T>) => T
function createUserFactory(): TestDataFactory<User> {
return (overrides = {}) => ({
id: '1',
name: 'João Silva',
email: 'joao@email.com',
age: 30,
isActive: true,
...overrides
})
}
// Uso nos testes
describe('UserComponent', () => {
const createUser = createUserFactory()
it('should render user name', () => {
const user = createUser({ name: 'Maria Santos' })
render(<UserComponent user={user} />)
expect(screen.getByText('Maria Santos')).toBeInTheDocument()
})
it('should handle inactive user', () => {
const user = createUser({ isActive: false })
render(<UserComponent user={user} />)
expect(screen.getByText('Usuário inativo')).toBeInTheDocument()
})
})🚀 Configuração avançada
1. tsconfig.json otimizado
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"],
"@/components/*": ["./src/components/*"],
"@/utils/*": ["./src/utils/*"],
"@/types/*": ["./src/types/*"]
},
"strictNullChecks": true,
"noImplicitAny": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}2. ESLint com TypeScript
{
"extends": [
"next/core-web-vitals",
"@typescript-eslint/recommended",
"@typescript-eslint/recommended-requiring-type-checking"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "./tsconfig.json"
},
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"@typescript-eslint/no-non-null-assertion": "warn",
"@typescript-eslint/prefer-nullish-coalescing": "error",
"@typescript-eslint/prefer-optional-chain": "error"
}
}📊 Performance e TypeScript
1. Type-only imports
// ❌ Import de valor desnecessário
import { User, createUser } from './user'
// ✅ Import apenas do tipo
import type { User } from './user'
import { createUser } from './user'
// ✅ Import de namespace
import type * as UserTypes from './user'2. Const assertions
// ❌ Tipo inferido como string[]
const colors = ['red', 'green', 'blue']
// ✅ Tipo inferido como readonly tuple
const colors = ['red', 'green', 'blue'] as const
// Uso com template literals
const themes = ['light', 'dark'] as const
type Theme = typeof themes[number] // 'light' | 'dark'✅ Checklist TypeScript Avançado
Generics:
- Dominar generics básicos
- Usar constraints (extends)
- Implementar generics em React components
- Criar APIs genéricas
Utility Types:
- Usar Partial, Required, Pick, Omit
- Aplicar Record, Exclude, Extract
- Dominar ReturnType, Parameters
- Criar utility types customizados
Conditional Types:
- Entender conditional types básicos
- Usar infer para inferir tipos
- Criar mapped types
- Implementar template literal types
Padrões Avançados:
- Usar discriminated unions
- Implementar branded types
- Criar function overloads
- Aplicar const assertions
Configuração:
- Otimizar tsconfig.json
- Configurar ESLint com TypeScript
- Usar type-only imports
- Implementar path mapping
🎯 Conclusão
TypeScript avançado não é apenas sobre tipos, é sobre criar código mais robusto, maintível e expressivo. As técnicas mostradas aqui vão elevar seu nível como desenvolvedor frontend.
Principais benefícios:
- 🐛 Menos bugs em produção
- 🧠 IntelliSense mais inteligente
- 🔄 Refactoring seguro
- 📚 Código auto-documentado
- 👥 Colaboração mais eficiente
Próximos passos:
- Pratique generics em projetos reais
- Implemente utility types no seu código
- Experimente conditional types
- Configure TypeScript strict mode
Lembre-se: TypeScript é uma ferramenta de produtividade, não um obstáculo! 🚀
Allisson Lima
Desenvolvedor Frontend | Especialista em TypeScript e Arquitetura