Skip to content

Programação Orientada a Objetos (POO) no TS Web

No desenvolvimento Front-end moderno, o paradigma funcional domina com React Hooks. No entanto, os conceitos da POO são fundamentais para organizar lógica de negócio e infraestrutura. No nosso projeto, evitamos o uso de classes (exceto para erros), preferindo interfaces, tipos e funções de fábrica (factories).


1. Abstração e Modelagem de Dados

Em vez de classes, usamos interface ou type para definir a estrutura dos dados e funções puras para operar sobre eles. Isso mantém os dados "leves" e fáceis de serializar/compartilhar entre estados.

Exemplo: Entidade de Domínio (POO Funcional)

typescript
// ✅ Como fazer: Interface + Factory + Funções de Domínio
export interface Patient {
  readonly id: string;
  readonly name: string;
  readonly birthDate: string; // ISO string
}

// Factory: Cria a "instância" (objeto literal)
export function createPatient(data: Partial<Patient>): Patient {
  return {
    id: data.id ?? crypto.randomUUID(),
    name: data.name ?? 'Sem nome',
    birthDate: data.birthDate ?? new Date().toISOString(),
  };
}

// Comportamento: Funções que recebem o dado (Abstração)
export function calculatePatientAge(patient: Patient): number {
  const birthDate = new Date(patient.birthDate);
  const today = new Date();
  let age = today.getFullYear() - birthDate.getFullYear();
  const monthDiff = today.getMonth() - birthDate.getMonth();

  if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
    age--;
  }

  return age;
}

export function isPatientOfLegalAge(patient: Patient): boolean {
  const legalAge = 18;
  return calculatePatientAge(patient) >= legalAge;
}

2. Pilares da POO sem Classes

A. Encapsulamento (Closures e Modules)

O encapsulamento garante que dados sensíveis não sejam alterados de fora. Em TS/Web, usamos Closures ou apenas a exportação seletiva de arquivos (Modules).

typescript
// ✅ Como fazer: Usando Closures para estado privado
export function createAuthService() {
  let _token: string | null = null; // "Privado" via Closure

  function setToken(token: string) {
    if (!token) throw new Error("Token inválido");
    _token = token;
    localStorage.setItem('auth_token', token);
  }

  function hasValidToken() {
    return Boolean(_token);
  }

  return {
    setToken,
    hasValidToken
  };
}

// Uso
const authService = createAuthService();
authService.setToken('abc-123');

B. Herança vs Composição

Em POO tradicional, usa-se extends. No React/TS moderno, preferimos Composição de Tipos e Slots.

typescript
// Composição de Tipos
type AppErrorBase = {
  message: string;
  code: number;
};

type ApiError = AppErrorBase & {
  endpoint: string;
};

C. Herança de Erros (A Exceção)

As Classes de Erro são a única exceção onde incentivamos o uso de classes, pois permite o uso do operador instanceof para captura tipada de erros.

typescript
// ✅ Como fazer: Única exceção para uso de classes
export class AppError extends Error {
  constructor(public message: string, public code: number) {
    super(message);
    this.name = "AppError";
  }
}

export class UnauthorizedError extends AppError {
  constructor() {
    super("Sessão expirada.", 401);
    this.name = "UnauthorizedError";
  }
}

D. Polimorfismo (Interfaces)

O polimorfismo no TS é atingido através da implementação de interfaces por diferentes objetos literais.

typescript
interface StorageProvider {
  save(key: string, value: string): void;
}

export function createBrowserStorage(): StorageProvider {
  return {
    save(key: string, value: string) {
      localStorage.setItem(key, value);
    }
  };
}

export function createMemoryStorage(): StorageProvider {
  return {
    save(key: string, value: string) {
      console.log('Saving to memory:', key, value);
    }
  };
}

3. Onde usar cada abordagem

No nosso projeto, seguimos estas diretrizes:

  1. UI (Telas e Componentes): Sempre Funcional.
  2. Entidades e Models: Interfaces + Objetos Literais (evite classes).
  3. Serviços e Infra: Objetos Literais ou Factories (evite classes).
  4. Tratamento de Erros: Classes (para usar instanceof).

❌ O que NÃO usar classes

  • API Models: Não crie classes que dão new no JSON da API. Use interfaces e mapeadores (mappers).
  • Componentes React: Proibido class extends React.Component.
  • Utils: Se é uma função, exporte a função diretamente.

Por que evitar classes no TS Web?

  1. Imutabilidade: Objetos literais funcionam melhor com o ecossistema de estado do React (Redux, TanStack) que exige imutabilidade. Classes muitas vezes incentivam mutação interna.
  2. Serialização: Classes perdem seus métodos quando enviadas via postMessage (WebWorkers) ou salvas no Redux/LocalStorage.
  3. Tamanho do Bundle: Funções e objetos literais sofrem melhor Tree Shaking do que classes.
  4. Sintaxe: O código fica mais conciso e alinhado ao padrão de Hooks do React.