Pular para o conteúdo
TypeScript

Strict Mode no TypeScript: Como Ativar

Ative o strict mode no TypeScript e elimine categorias inteiras de bugs. Guia prático com cada flag explicada, exemplos reais e estratégia de adoção incremental.

O Que É o Strict Mode no TypeScript

O strict mode é uma flag do tsconfig.json que liga 8 verificações de segurança de uma vez. É como colocar o TypeScript no modo mais rigoroso possível. Cada flag pega uma categoria específica de bug.

Quando você adiciona "strict": true no compilerOptions, o TypeScript ativa: strictNullChecks, noImplicitAny, strictBindCallApply, strictFunctionTypes, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables e alwaysStrict.

Dá pra ativar cada flag individualmente. Isso é útil pra adoção gradual em projetos existentes. Mas em projetos novos, vai de strict: true direto. Sem desculpa.

Como Ativar o Strict Mode Passo a Passo

Se o projeto é novo, ativa tudo de uma vez. Se já existe, vai aos poucos. Aqui vai a estratégia pra cada cenário.

  1. Passo 1 - Projeto novo — ative strict: true direto: Adicione "strict": true no compilerOptions do tsconfig.json. Pronto, todas as 8 flags estão ativas. Escreva código seguro desde o começo.
  2. Passo 2 - Projeto existente — comece por strictNullChecks: Essa flag sozinha já pega a maioria dos bugs. Ative, corrija os erros que aparecem e estabilize antes de ativar a próxima.
  3. Passo 3 - Ative noImplicitAny: Proíbe variáveis com tipo implícito any. O TypeScript vai pedir tipos explícitos onde a inferência não resolve. Tipar os parâmetros de função é a maior parte do trabalho.
  4. Passo 4 - Ative as flags restantes: strictFunctionTypes, strictPropertyInitialization e as outras. Cada uma pega bugs mais específicos. Ative uma por vez e corrija antes de seguir.
  5. Passo 5 - Substitua por strict: true: Quando todas as flags estiverem ativas individualmente, troque por strict: true. Mais limpo e garante que flags novas do TypeScript sejam incluídas automaticamente em atualizações futuras.

Cada Flag Explicada com Exemplos

Vamos ver o que cada flag faz na prática. Código real mostra melhor que descrições abstratas.

strictNullChecks — A Flag Mais Impactante

// SEM strictNullChecks (perigoso)
function buscarUsuario(id: string): User {
  // Pode retornar undefined, mas o tipo diz User
  return database.find(u => u.id === id);
}

const user = buscarUsuario('123');
console.log(user.name); // Runtime error: Cannot read property 'name' of undefined

// COM strictNullChecks (seguro)
function buscarUsuario(id: string): User | undefined {
  return database.find(u => u.id === id);
}

const user = buscarUsuario('123');
// console.log(user.name); // Erro de compilação!

// Jeito correto:
if (user) {
  console.log(user.name); // OK - TypeScript sabe que não é undefined
}

// Ou com optional chaining:
console.log(user?.name ?? 'Usuário não encontrado');

Essa flag sozinha previne a maioria dos erros de 'Cannot read property of undefined'. O TypeScript te obriga a verificar null/undefined antes de acessar propriedades. Parece chato, mas cada verificação é um bug a menos em produção.

noImplicitAny — Nada de Any Escondido

// SEM noImplicitAny (o TypeScript aceita calado)
function processar(dados) {
  // 'dados' é implicitamente 'any'
  // Qualquer operação passa sem checagem
  return dados.map(item => item.valor * 2);
}

// COM noImplicitAny (TypeScript exige tipo)
function processar(dados) {
  // Erro: Parameter 'dados' implicitly has an 'any' type
}

// Jeito correto:
interface Item {
  valor: number;
}

function processar(dados: Item[]): number[] {
  return dados.map(item => item.valor * 2);
}

// Agora se alguém passa um array de strings, o compilador barra
// processar(['a', 'b']); // Erro de compilação

strictFunctionTypes — Compatibilidade de Funções

// strictFunctionTypes verifica compatibilidade de callbacks

interface Animal {
  nome: string;
}

interface Cachorro extends Animal {
  raca: string;
}

// SEM strictFunctionTypes (deixa passar)
type Handler = (animal: Animal) => void;

const handleCachorro: Handler = (cachorro: Cachorro) => {
  console.log(cachorro.raca); // Perigoso! Nem todo Animal tem raca
};

// COM strictFunctionTypes (barra)
const handleCachorro: Handler = (cachorro: Cachorro) => {
  // Erro: Type '(cachorro: Cachorro) => void' is not assignable
  // to type 'Handler'
  console.log(cachorro.raca);
};

// Jeito correto: use Animal e verifique
const handleAnimal: Handler = (animal: Animal) => {
  if ('raca' in animal) {
    console.log((animal as Cachorro).raca);
  }
};

strictPropertyInitialization — Classes Seguras

// SEM strictPropertyInitialization
class UserService {
  private db: Database; // Sem erro, mas db pode ser undefined

  getUsers() {
    return this.db.query('SELECT * FROM users');
    // Runtime error se db não foi inicializado!
  }
}

// COM strictPropertyInitialization
class UserService {
  private db: Database;
  // Erro: Property 'db' has no initializer and is not
  // definitely assigned in the constructor

  // Opção 1: Inicializar no constructor
  constructor(db: Database) {
    this.db = db;
  }

  // Opção 2: Valor padrão
  private retries: number = 3;

  // Opção 3: Definite assignment assertion (quando você tem certeza)
  private config!: Config; // O ! diz "confie em mim"
}

useUnknownInCatchVariables — Catch Seguro

// SEM useUnknownInCatchVariables
try {
  JSON.parse(texto);
} catch (error) {
  // error é 'any' - aceita qualquer coisa
  console.log(error.message); // Funciona, mas sem segurança
}

// COM useUnknownInCatchVariables
try {
  JSON.parse(texto);
} catch (error) {
  // error é 'unknown' - precisa verificar
  // console.log(error.message); // Erro!

  if (error instanceof Error) {
    console.log(error.message); // Agora sim, seguro
  } else {
    console.log('Erro desconhecido:', String(error));
  }
}

// Por que isso importa? Nem todo throw lança Error:
// throw "string de erro";
// throw 42;
// throw { custom: true };
// O catch precisa estar preparado pra qualquer coisa

Cada flag cobre uma brecha diferente. Juntas, elas formam uma rede de segurança que pega bugs em tempo de compilação — onde o custo de correção é próximo de zero.

Adoção Incremental em Projetos Existentes

Ligar strict: true num projeto grande de uma vez vai gerar centenas de erros. A estratégia certa é ativar uma flag por vez, corrigir e estabilizar antes de seguir.

// tsconfig.json - Adoção incremental
{
  "compilerOptions": {
    // Fase 1: Comece com essas
    "strictNullChecks": true,
    "noImplicitAny": true,

    // Fase 2: Adicione depois
    "strictFunctionTypes": true,
    "strictBindCallApply": true,

    // Fase 3: Finalize
    "strictPropertyInitialization": true,
    "noImplicitThis": true,
    "useUnknownInCatchVariables": true,
    "alwaysStrict": true

    // Fase 4: Substitua tudo por
    // "strict": true
  }
}

// Dica: use @ts-expect-error pra erros que vai corrigir depois
function legacyFunction(data: any) {
  // @ts-expect-error - TODO: tipar corretamente na sprint 15
  return data.process();
}

O @ts-expect-error é melhor que @ts-ignore pra migração. Quando você corrige o erro, o @ts-expect-error avisa que não é mais necessário. O @ts-ignore fica lá pra sempre, escondendo problemas que talvez já não existam.

Benefícios Práticos do Strict Mode

Números concretos do impacto de ativar strict mode em projetos reais.

// Impacto documentado:
//
// Benefícios observados na prática (não são estatística auditada):
// - Menos crashes de null/undefined com strictNullChecks
// - Bugs de tipo aparecem no editor, não só em produção
// - Refatoração mais segura porque o compilador pega regressões
//
// Antes do strict mode:
function getConfig() {
  return config; // Pode ser undefined, ninguém sabe
}
const port = getConfig().port; // Bomba em produção

// Depois do strict mode:
function getConfig(): Config | undefined {
  return config;
}
const port = getConfig()?.port ?? 3000; // Seguro, com fallback

// O strict mode não te impede de nada.
// Ele te obriga a ser explícito sobre as decisões.
// "Esse valor pode ser null? Trate antes de usar."
// "Esse parâmetro aceita qualquer tipo? Defina qual."

Strict mode não é sobre restrição — é sobre clareza. O código fica mais explícito sobre o que pode dar errado. E quando as coisas são explícitas, ficam mais fáceis de manter, testar e refatorar.

Erros Comuns ao Usar Strict Mode

Tropeços frequentes com strict

Desativar strict porque 'dá muito erro': os erros já existem no código. Strict só torna eles visíveis. Desativar é como desligar o alarme de incêndio porque faz barulho.

Usar as any pra silenciar erros: type assertion pra any é pior que não ter strict. Você perde toda a segurança naquele ponto. Use narrowing (typeof, instanceof, in) pra resolver os erros de tipo corretamente.

Abusar do operador ! (non-null assertion): user!.name diz pro TypeScript 'confia, não é null'. Se for null, crash em runtime. Use ! só quando você tem certeza absoluta — e mesmo assim, prefira verificação explícita.

Não configurar strictPropertyInitialization com DI: frameworks de injeção de dependência inicializam propriedades fora do constructor. Use o ! (definite assignment) nesses casos: private service!: MyService.

Ativar tudo de uma vez em projeto legado: centenas de erros desmotivam o time. Vá flag por flag, sprint por sprint. Progresso constante é melhor que revolução frustrada.

Checklist do Strict Mode

  • strict: true ativado no tsconfig.json (ou flags individuais em migração)
  • strictNullChecks ativo e verificações de null/undefined no código
  • noImplicitAny ativo e parâmetros de função tipados
  • strictFunctionTypes ativo com callbacks tipados corretamente
  • strictPropertyInitialization ativo com propriedades de classe inicializadas
  • useUnknownInCatchVariables ativo com tratamento de catch tipado
  • Zero @ts-ignore no código (usar @ts-expect-error se necessário)
  • CI configurado pra rodar tsc --noEmit e barrar erros de tipo

TypeScript Seguro na Prática

Strict mode é o que separa projetos TypeScript amadores de profissionais. No CrazyStack, todo o código roda com strict: true desde o primeiro commit. Você aprende a lidar com null checks, tipagem de funções e classes seguras construindo um projeto real do zero.

Não tem atalho pra código seguro. Mas com strict mode ativado, o compilador trabalha junto com você.

Perguntas frequentes

O Que É o Strict Mode no TypeScript

O strict mode é uma flag do tsconfig.json que liga 8 verificações de segurança de uma vez. É como colocar o TypeScript no modo mais rigoroso possível. Cada flag pega uma categoria específica de bug. Quando você adiciona "strict": true no compilerOptions, o TypeScript ativa: strictNullChecks, noImplicitAny, strictBindCallApply, strictFunctionTypes, strictPropertyInitialization, noImplicitThis, useUnknownInCatchVariables e alwaysStrict. Dá pra ativar cada flag individualmente. Isso é útil pra adoção gradual em projetos existentes. Mas em projetos novos, vai de strict: true direto. Sem desculpa.

Como Ativar o Strict Mode Passo a Passo

Se o projeto é novo, ativa tudo de uma vez. Se já existe, vai aos poucos. Aqui vai a estratégia pra cada cenário.