TypeScript: Melhores práticas do TypeScript

Última atualização: 2026-08-26

Dominar a gramática é apenas o começo — para escrever um bom código em TypeScript, é preciso seguir um conjunto de práticas recomendadas. Esta lição resume as ideias e os princípios mais valiosos extraídos de projetos reais.

1. Convenções de nomenclatura

(1) Nomeação de tipos

Categoria Especificação Exemplo
Interface PascalCase UserService, ApiResponse
Alias de tipo PascalCase Status, EventHandler
Parâmetro genérico Letra única ou PascalCase T, K, TItem
Enumeração PascalCase (os valores podem estar em maiúsculas) HttpStatus, COLOR_RED
Elementos da enumeração PascalCase HttpStatus.Ok

(2) Nomeação de arquivos

Tipo Especificação Exemplo
Módulo regular camelCase userService.ts
Tipo de arquivo PascalCase UserController.ts
Arquivo de declaração Mesmo nome do módulo lodash.d.ts
Arquivo de teste Nome do módulo.test userService.test.ts

(3) Especificações de exportação

TYPESCRIPT
// ✅ Recommendations——Prioritize naming and exporting
export function addUser(user: User): void { }
export class UserController { }
export type Status = "active" | "inactive";

// ⚠️ Use Default Export with Caution——It's easy to make mistakes when renaming files
export default class UserController { }

// ✅ Library/Framework recommended — both options are available
export class UserController { }
export default UserController;


2. Princípios do design tipográfico

(1) Princípio 1: Precisão em vez de abrangência

TYPESCRIPT
// ❌ Broad——Type information lost
function process(value: any): any { }

// ❌ Slightly better, but still too broad
function process(value: string | number): string | number { }

// ✅ Accurate——Generics Preserve Type Information
function process<T extends string | number>(value: T): T { }

(2) Princípio 2: É melhor fazer cálculos do que escrever à mão

TYPESCRIPT
// ❌ Manually maintain two locations——Prone to desynchronization
interface User { id: number; name: string; email: string; }
type UserKeys = "id" | "name" | "email";

// ✅ Inference Based on Source Type——Automatic Synchronization
type UserKeys2 = keyof User;  // "id" | "name" | "email"
type UserValues = User[keyof User];  // number | string

(3) Princípio 3: Composição em vez de herança

TYPESCRIPT
// ❌ Deep Inheritance——The Problem with Fragile Base Classes
class BaseEntity { id: number; }
class TimestampedEntity extends BaseEntity { createdAt: Date; }
class FullEntity extends TimestampedEntity { createdBy: string; }

// ✅ Type Combinations——Flexible and decoupled
type WithId = { id: number };
type WithTimestamps = { createdAt: Date; updatedAt: Date };
type WithAudit = { createdBy: string; updatedBy: string };

type FullEntity2 = WithId & WithTimestamps & WithAudit;
type SimpleEntity = WithId;  // Customizable Combinations

▶ Exemplo: Refatoração de “any” para garantir segurança de tipos

TYPESCRIPT
// ❌ Before the refactoring——everywhere any,Zero Type Safety
function processRequest(req: any): any {
  let user = req.body.user;       // any
  let result = validate(user);    // any
  return { status: 200, data: result };
}

// ✅ After the refactoring——Type safety at every step
interface User { id: number; name: string; email: string; }
interface Request2 { body: { user: User } }
interface ValidationResult { valid: boolean; errors?: string[] }
interface Response2<T> { status: number; data: T }

function processRequest2(req: Request2): Response2<ValidationResult> {
  let user: User = req.body.user;             // ✅ User Type
  let result: ValidationResult = validate2(user);  // ✅ ValidationResult
  return { status: 200, data: result };
}

function validate2(user: User): ValidationResult {
  if (!user.email.includes("@")) {
    return { valid: false, errors: ["Invalid email address"] };
  }
  return { valid: true };
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
// Executed successfully


3. Alternativas ao any

(1) desconhecido como alternativa a qualquer

TYPESCRIPT
// ❌ any——Turn off type checking
function process(value: any) {
  return value.toUpperCase();  // Do not check,May crash during runtime
}

// ✅ unknown——You must narrow it first before you can use it.
function process2(value: unknown) {
  if (typeof value === "string") {
    return value.toUpperCase();  // ✅ Safe Use After Narrowing
  }
  throw new Error("Expectations string Type");
}

(2) Genéricos como substituto para any

TYPESCRIPT
// ❌ any——Missing Type Information
function first(arr: any[]): any {
  return arr[0];
}

// ✅ Generics——Preserve Type Information
function first2<T>(arr: T[]): T {
  return arr[0];
}

(3) Usando tipos de união em vez de any

TYPESCRIPT
// ❌ any
let value: any;

// ✅ Composite Types——Clearly list the possible types
let value2: string | number | boolean;

(4) Substituição de objetos any por assinaturas de índice

TYPESCRIPT
// ❌ any Object
let config: any = { host: "localhost" };

// ✅ Index Signature
let config2: Record<string, string | number> = { host: "localhost" };


4. Princípio DRY (Não se repita)

(1) Use keyof, typeof e tipos de utilitários para evitar duplicação

TYPESCRIPT
const THEMES = {
  light: { bg: "#fff", text: "#333" },
  dark: { bg: "#1a1a1a", text: "#e0e0e0" }
} as const;

// Type Inference from Values——No duplicate definitions
type ThemeName = keyof typeof THEMES;  // "light" | "dark"
type ThemeColors = typeof THEMES["light"];  // { readonly bg: "..."; readonly text: "..." }

function getTheme(name: ThemeName): ThemeColors {
  return THEMES[name];
}

(2) Transformações em lote utilizando tipos de mapeamento

TYPESCRIPT
interface ApiUser {
  id: number;
  name: string;
  email: string;
  role: string;
}

// No handwriting required——Derivation Using Tool Types
type CreateUserDTO = Omit<ApiUser, "id">;
type UpdateUserDTO = Partial<Omit<ApiUser, "id">>;
type UserSummary = Pick<ApiUser, "id" | "name">;
type UserResponse = Readonly<ApiUser>;


5. Estratégias de restrição de tipos

(1) Restringir o mais cedo possível

TYPESCRIPT
// ❌ The gap is narrowing——Check each location before use
function process(value: string | number) {
  console.log(value.toString());      // Only shared methods can be used
  if (typeof value === "string") {
    console.log(value.toUpperCase());
  }
  // Further checks will be needed later....
}

// ✅ Narrow as soon as possible——Use directly within the branch
function process2(value: string | number) {
  if (typeof value === "string") {
    // The entire branch is string
    console.log(value.toUpperCase());
    console.log(value.trim());
    return;
  }
  // This must be number
  console.log(value.toFixed(2));
}

(2) Reutilização da lógica de restrição em verificações de tipo personalizadas

TYPESCRIPT
// The Complex Logic Behind Narrowing——Extract as a type guard
function isValidUser(obj: any): obj is User {
  return obj
    && typeof obj.id === "number"
    && typeof obj.name === "string"
    && typeof obj.email === "string";
}

// Reuse in Multiple Places
function processUser(data: unknown) {
  if (isValidUser(data)) {
    console.log(data.name);  // ✅ Type Safety
  }
}


6. Diretrizes para a colaboração em equipe

(1) Configuração unificada do tsconfig

JSON
{
  "compilerOptions": {
    "strict": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true
  }
}

As mesmas regras são aplicadas automaticamente aos IDEs de todos os membros da equipe — não é necessária nenhuma configuração manual.

(2) Regras do ESLint para TypeScript

JSON
{
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:@typescript-eslint/recommended-requiring-type-checking"
  ],
  "rules": {
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-unnecessary-type-assertion": "error",
    "@typescript-eslint/explicit-function-return-type": "warn"
  }
}

(3) Lista de verificação para revisão de código



▶ Exemplo: Modo estrito capturando bugs reais

TYPESCRIPT
// strict: true catches null/undefined bugs at compile time
interface SearchResult { items: string[]; total: number; }

function search(query: string): SearchResult | null {
  if (!query.trim()) return null;
  return { items: [`Result for ${query}`], total: 1 };
}

// Without strict: compiles but crashes at runtime
// let result = search("");
// console.log(result.items.length); // TypeError at runtime

// With strict: compiler forces null check
let result = search("");
if (result) {
  console.log(result.items.length); // ✅ safe after narrowing
} else {
  console.log("No query provided");
}
▶ Experimente

Saída:

TEXT 📖 Somente leitura
No query provided

▶ Exemplo: Eliminando any com genéricos

TYPESCRIPT
// ❌ Before: loose types with any
function getProp(obj: any, key: string): any {
  return obj[key];
}

let user: any = { name: "Alice", age: 30 };
let name2 = getProp(user, "name"); // any — no autocomplete

// ✅ After: generics preserve type information
function getProp2<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

let user2 = { name: "Alice", age: 30 } as const;
let name3 = getProp2(user2, "name"); // "Alice" — exact literal type
let age = getProp2(user2, "age");    // 30 — exact literal type
// getProp2(user2, "email");         // ❌ not assignable to keyof
▶ Experimente

Saída:

TEXT 📖 Somente leitura
Compile error — Argument of type "email" is not assignable to keyof

❓ Perguntas Frequentes

P: O uso de any deve ser totalmente proibido em um projeto? R: Isso não é realista. Configure o ESLint para no-explicit-any: warn em vez de error — isso permite um pequeno número de usos de any, mas solicita uma revisão. any é aceitável em cenários como bibliotecas de terceiros sem tipagem, conversões de tipos complexas e prototipagem rápida. O importante é incluir comentários explicando o motivo e ter um plano para a substituição.

P: As definições de tipos devem ser colocadas em um arquivo separado ou dentro do arquivo em que são utilizadas? R: Os tipos públicos/compartilhados devem ser colocados no diretório types/ ou models/; os tipos utilizados apenas dentro de um único arquivo devem ser definidos diretamente nesse arquivo. Regra: Se um tipo for referenciado por três ou mais arquivos, extraia-o para um arquivo de tipos públicos; caso contrário, defina-o no próprio arquivo em que é usado.

P: Devo usar o ESLint em um projeto TypeScript? R: Sim. O tsc lida com a verificação de tipos, enquanto o ESLint lida com as verificações de qualidade do código — os dois se complementam, em vez de se substituírem. O plugin @typescript-eslint oferece regras específicas para TypeScript (como no-explicit-any e consistent-type-imports), tornando-o uma escolha padrão para projetos em TypeScript.

P: O que devo fazer se houver muitos parâmetros de tipo genérico? R: Se houver mais de três parâmetros de tipo, considere o seguinte: (1) Substitua vários genéricos por parâmetros de objeto; (2) Extraia alguns tipos para interfaces separadas; (3) Use valores padrão genéricos para reduzir o número de parâmetros que devem ser especificados. Um número excessivo de parâmetros de tipo geralmente indica um nível inadequado de abstração.

📖 Resumo

📝 Exercícios

  1. Exercício básico (Dificuldade ⭐): Encontre um exemplo do tipo any que você já tenha escrito ou visto e substitua-o por unknown ou por tipos genéricos. Certifique-se de que a funcionalidade permaneça a mesma após a substituição e que o código fique mais seguro em termos de tipos.
  2. Problema avançado (Dificuldade ⭐⭐): Refatore um conjunto de definições de tipos usando o princípio DRY — dada a interface ApiProduct, use tipos utilitários para derivar os quatro tipos CreateProduct, UpdateProduct, ProductSummary e ProductResponse, sem escrever manualmente nenhuma propriedade duplicada.
  3. Desafio (Dificuldade: ⭐⭐⭐): Elabore um documento de padrões de codificação em TypeScript para sua equipe — incluindo convenções de nomenclatura, alternativas ao any, regras de importação de tipos, configurações recomendadas do tsconfig e regras recomendadas do ESLint. Justifique cada regra, acompanhada de exemplos e contraexemplos.
Web-Tutorial.com

Equipe Técnica Web-Tutorial

Uma plataforma de tutoriais mantida por diversos desenvolvedores. Cada tutorial é escrito e revisado por profissionais da área correspondente. Trabalhamos para manter nosso conteúdo preciso e confiável — se encontrar algum problema, avise-nos.

100%