af aprenda frontend
módulo 05 tipos

TypeScript: tipos que acompanham o código.

Checklist final: strict: true, evitar any, preferir inferência, modelar dados com tipos antes de escrever lógica.

O módulo converteu os quatro arquivos JavaScript do blog — utils.js, api.js, ui.js e main.js — para TypeScript. O blog não mudou visualmente. O que mudou é que erros de tipo, que antes só apareceriam em runtime (e frequentemente no browser do usuário), agora aparecem no editor durante o desenvolvimento. Esta lição consolida os conceitos percorridos e estabelece o checklist para o próximo módulo.

O que mudou no blog

A migração de .js para .ts foi incremental — cada arquivo ganhou tipos sem mudar a lógica. O types.ts foi o ponto de partida: as interfaces Artigo, Autor e Tag, e os type aliases Tema e StatusArtigo. Com esses tipos definidos, os demais módulos os importaram e o compilador passou a verificar toda interação com dados de artigos.

ts
// utils.js — JavaScript original
function calcularTempoLeitura(palavras) {
  return Math.ceil(palavras / 238);
}

// utils.ts — TypeScript
function calcularTempoLeitura(palavras: number): number {
  return Math.ceil(palavras / 238);
}

// a diferença é pequena no código, mas o efeito é grande:
// chamar calcularTempoLeitura("muito texto") agora é erro de compilação
// chamar calcularTempoLeitura() sem argumento também é erro
// o editor mostra o tipo esperado ao digitar a chamada

// api.ts — o mais crítico
async function buscarArtigos(): Promise<Artigo[]> {
  const response = await fetch("/artigos.json");
  if (!response.ok) throw new Error(`Erro ${response.status}`);
  return response.json() as Promise<Artigo[]>;
}

// agora qualquer código que usa buscarArtigos() sabe que recebe Artigo[]
// acessar .descricao em vez de .titulo? erro de compilação
// passar o resultado para uma função que espera Autor[]? erro de compilação
Antes e depois — o diff que importa.

O compilador se tornou o guardião da consistência entre módulos: se você alterar a interface Artigo (adicionar um campo, mudar um tipo), todos os lugares que usam Artigo recebem erros de compilação até serem atualizados. Um refactor que antes exigia grep manual agora tem cobertura automática.

Checklist TypeScript

Estas práticas aparecem ao longo do módulo com razões específicas — cada uma protege contra uma classe de erro concreta:

ts
// ✅ strict: true no tsconfig.json
// sem strict, null e undefined passam sem verificação e any aparece silenciosamente

// ✅ interfaces para todos os objetos de domínio — types.ts
export interface Artigo {
  id: number;
  titulo: string;
  publicadoEm: string | null; // null declarado explicitamente — não é surpresa
  readonly slug: string;       // readonly — o compilador impede reatribuição acidental
}

// ✅ anote nas bordas — parâmetros de funções exportadas
export function formatarData(dateString: string): string { /* ... */ }
export function renderizarCard(artigo: Artigo, curtido: boolean): string { /* ... */ }

// ✅ infira no interior — variáveis temporárias e callbacks
const publicados = artigos.filter(a => a.publicado);  // Artigo[] — inferido
const titulos = publicados.map(a => a.titulo);         // string[] — inferido

// ✅ unknown no catch — com strict, o padrão moderno
try {
  await buscarArtigos();
} catch (erro) {
  if (erro instanceof Error) {
    exibirNotificacao(erro.message);
  }
}

// ✅ as const para configurações e conjuntos de valores
const STATUS = { Rascunho: "rascunho", Publicado: "publicado" } as const;
type Status = typeof STATUS[keyof typeof STATUS]; // "rascunho" | "publicado"

// ✅ discriminated unions para estados
type EstadoCarregamento =
  | { status: "carregando" }
  | { status: "sucesso"; artigos: Artigo[] }
  | { status: "erro"; mensagem: string };

// ✅ tipos utilitários em vez de duplicar interfaces
type FormularioEdicao = Partial<Omit<Artigo, "id" | "slug" | "curtidas">>;

// ✅ evitar any — sempre há uma alternativa melhor
// any → unknown (dados desconhecidos)
// any → generic <T> (código reutilizável)
// any → tipo específico (quando você sabe o que é)
Checklist embutido no código — as decisões que importam.

A relação com o módulo de React

O TypeScript aprendido aqui continua no React — os mesmos conceitos, aplicados a um novo contexto. As interfaces viram tipos de props, os generics aparecem em hooks, os discriminated unions descrevem estados de componente:

ts
// no blog:
interface Artigo { id: number; titulo: string; /* ... */ }
function renderizarCard(artigo: Artigo): string { /* ... */ }

// no React — a mesma lógica, com JSX:
interface Props {
  artigo: Artigo;
  curtido: boolean;
  onCurtir: (id: number) => void;
}

function CardArtigo({ artigo, curtido, onCurtir }: Props) {
  return (
    <article>
      <h2>{artigo.titulo}</h2>
      <button onClick={() => onCurtir(artigo.id)}>
        {curtido ? "❤️" : "🤍"}
      </button>
    </article>
  );
}

// o useState é genérico — inferência automática
const [curtidos, setCurtidos] = useState<number[]>([]);
// curtidos é number[] — o mesmo tipo que localStorage.getItem no blog
O que o TypeScript do blog vira no React.

O modelo mental muda de “manipular o DOM diretamente” para “descrever como a UI deve ser dado o estado”. O TypeScript acompanha essa mudança sem exigir reaprendizado — você continua usando interface, type, generics e unions. O que muda é o destino: em vez de el.textContent = artigo.titulo, você retorna <h2>{artigo.titulo}</h2> de um componente.

Resumo

  • strict: true é o ponto de partida — habilita strictNullChecks e noImplicitAny, sem os quais o TypeScript não detecta os erros mais comuns.
  • Interfaces de domínio (Artigo, Autor, Tag) são a fonte da verdade — defina-as primeiro, escreva a lógica depois. Quando a interface muda, o compilador aponta todos os chamadores desatualizados.
  • Anote nas bordas (parâmetros de funções exportadas, respostas de API), infira no interior (variáveis temporárias, callbacks de map/filter).
  • unknown em vez de any para dados externos — exige narrowing ou cast explícito, mantendo a proteção.
  • as const para configurações e conjuntos de valores — cria tipos literais e readonly com zero custo em runtime.
  • Discriminated unions para estados de carregamento — { status: "carregando" } | { status: "sucesso"; dados: T } | { status: "erro"; mensagem: string }.
  • Tipos utilitários (Partial, Omit, Pick, Record, ReturnType) para derivar variações de interfaces sem duplicar.
  • React é o próximo módulo: os mesmos tipos TypeScript, aplicados a props de componentes, hooks tipados e estado declarativo.
/ checkpoint verifique seu entendimento
questão 1 de 4

Por que strict: true é a primeira configuração a ativar em um projeto novo?