A maioria dos projetos TypeScript que analiso tem "strict": true no tsconfig.json — mas também tem // @ts-ignore espalhados pelo código, any em lugares estratégicos, e engenheiros que não sabem exatamente o que o strict mode está protegendo.
Strict mode não é um conjunto arbitrário de restrições para fazer o compilador chato. É uma coleção de flags, cada uma capturando uma categoria específica de bug que acontece em runtime. Entender o que cada flag faz transforma "o TypeScript me obrigou a fazer isso" em "o TypeScript me impediu de cometer esse erro".
O que "strict": true habilita
"strict": true é um atalho para habilitar um conjunto de flags. Vamos pelo que realmente importa:
strictNullChecks
A mais importante. Sem ela, null e undefined são assignáveis a qualquer tipo. Com ela, você precisa lidar explicitamente com a possibilidade de ausência.
// Sem strictNullChecks — compila, pode explodir em runtime
function getUser(id: string): User {
return db.find(id) // pode retornar undefined
}
const name = getUser('123').name // TypeError: Cannot read property 'name' of undefined
// Com strictNullChecks — erro em tempo de compilação
function getUser(id: string): User | undefined {
return db.find(id)
}
const user = getUser('123')
const name = user?.name // forçado a lidar com o undefined
noImplicitAny
Força a tipagem explícita quando o TypeScript não consegue inferir o tipo. Sem isso, parâmetros de função sem tipo viram any silenciosamente — e você perde toda a proteção do sistema de tipos.
// ❌ Sem noImplicitAny — param é `any` implicitamente
function process(data) { // param: any
return data.value.toUpperCase() // sem verificação
}
// ✅ Com noImplicitAny — você declara a intenção
function process(data: { value: string }) {
return data.value.toUpperCase()
}
strictFunctionTypes
Garante verificação correta de tipos em callbacks. Sem ela, funções são verificadas bivariantly — o que pode deixar bugs de tipo passar.
// strictFunctionTypes captura este bug
type Handler = (event: MouseEvent) => void
const myHandler: Handler = (event: Event) => {} // ❌ Erro correto — MouseEvent é mais específico
strictPropertyInitialization
Garante que propriedades de classe sejam inicializadas no constructor. Previne leituras de propriedades undefined que parecem definidas.
class UserService {
private db: Database // ❌ Erro: não inicializado no constructor
constructor() {
// esqueceu de inicializar db
}
}
// ✅ Correto
class UserService {
private db: Database
constructor(db: Database) {
this.db = db
}
}
Flags que não estão no strict mas que você deveria habilitar
noUncheckedIndexedAccess
Acessos a arrays e objetos com index retornam T | undefined ao invés de T. Previne um dos bugs mais comuns: assumir que um index sempre existe.
// tsconfig.json
{ "compilerOptions": { "noUncheckedIndexedAccess": true } }
const items = ['a', 'b', 'c']
const first = items[0] // tipo: string | undefined (não string)
const safe = first?.toUpperCase() // forçado a verificar
exactOptionalPropertyTypes
Diferencia { prop?: string } (prop pode estar ausente) de { prop: string | undefined } (prop presente mas undefined). São coisas diferentes, e sem essa flag, TypeScript trata igual.
Como migrar um projeto legado para strict
Habilitar strict de uma vez num projeto grande não é prático. A estratégia que funciona:
- Habilite
"strict": truee use// @ts-ignoreem tudo que quebrar (sim, temporariamente) - Crie uma regra de lint que proíbe novos
// @ts-ignore— o legado pode existir, o novo não - Gradualmente, resolva os
@ts-ignorearquivo por arquivo, priorizando os mais críticos
Isso dá o benefício imediato de strict no código novo, enquanto o legado é corrigido de forma incremental.
Por que não desabilitar o que você não entende
Cada vez que você adiciona // @ts-ignore, as any, ou desabilita uma flag, você está dizendo: "Confio em mim mesmo mais do que no compilador aqui." Às vezes isso é legítimo — ao trabalhar com bibliotecas mal tipadas, por exemplo.
Na maioria das vezes, é fuga. E o TypeScript está te protegendo de algo que vai explodir em runtime — talvez não amanhã, talvez em 6 meses, talvez em produção numa sexta às 23h.
Quando você não entende por que o TypeScript está reclamando, a resposta certa é entender, não silenciar. Isso é a diferença entre usar TypeScript e usar TypeScript bem.