Introdução: por que o TypeScript reclama das suas propriedades?
Você está escrevendo código em TypeScript, tudo indo muito bem... E de repente — puf! Uma linha vermelha ondulada sob a propriedade da classe, e o compilador exibe a frase arrepiante: Property '...' has no initializer and is not definitely assigned in the constructor. Familiar? Se você já trabalhou pelo menos uma vez com Angular, React ou apenas com classes TypeScript — provavelmente sim.
Este erro é um dos mais populares no Stack Overflow (milhares de visualizações, dezenas de respostas). Mas a boa notícia: ele não é assustador. A má notícia: se você não entender, pode acabar causando bugs em tempo de execução. Neste artigo, vamos analisar por que esse erro do TypeScript ocorre, como corrigi-lo e quando cada uma das soluções é adequada.
O que significa o erro "Property has no initializer"?
Vamos traduzir do "compiladorês" para o português. O TypeScript está dizendo: "Ei, você declarou uma propriedade na classe, mas não deu um valor inicial e não a atribuiu no construtor. Não tenho certeza se haverá algo significativo lá quando você acessar esta propriedade".
Ative strict: true no tsconfig.json — e você automaticamente ativará a flag strictPropertyInitialization. É ela a responsável por essa verificação. O código se parece com isto:
class User { name: string; // ❌ Erro! age: number; // ❌ Erro!}O TypeScript observa, com razão: name e age podem acabar sendo undefined se você esquecer de inicializá-los. E o tipo deles é estrito string e number, sem undefined. Aí está o conflito.
Método nº1: Inicialização direta na declaração
O caminho mais simples e seguro é dar um valor à propriedade imediatamente. Isso é especialmente útil para campos com valores padrão.
class User { name: string = 'Convidado'; age: number = 0; createdAt: Date = new Date();}Quando usar: quando o campo tem um valor padrão razoável. Por exemplo, um contador, uma flag, uma data de criação. Esta é a melhor prática do TypeScript — você indica explicitamente o estado inicial.
Método nº2: Inicialização no construtor
O clássico do gênero. Se o valor deve vir de fora — atribua no construtor. O TypeScript aprova isso.
class User { name: string; age: number;
constructor(name: string, age: number) { this.name = name; this.age = age; }}Importante: o compilador verificará se você atribuiu um valor a todas as propriedades. Se esquecer uma — erro novamente.
class User { name: string; age: number;
constructor(name: string) { this.name = name; // age não foi atribuído — erro! }}Isso foi feito de propósito: o TypeScript protege você de um undefined acidental. Ou você inicializa todos os campos, ou permite explicitamente que eles sejam indefinidos.
Método nº3: Operador definite assignment (!)
Às vezes você sabe exatamente que a propriedade será inicializada mais tarde, mas não no construtor. Por exemplo, através de injeção de dependências no Angular ou através da chamada de um método assíncrono. Nesse caso, use a definite assignment assertion — um ponto de exclamação após o nome da propriedade.
class UserService { user!: User; // OK, eu prometo que ela será atribuída
constructor() { this.initUser(); }
initUser() { this.user = new User('Alice', 30); }}Atenção: esta é uma ferramenta poderosa, mas perigosa. Você está dizendo ao TypeScript: "Não verifique, eu sei o que estou fazendo". Se você esquecer de inicializar a propriedade — receberá undefined em tempo de execução. Use apenas quando tiver 100% de certeza.
Método nº4: Tornar a propriedade opcional (?)
Se