skip to content

A TypeScript class compiled with strict reports "Property 'name' has no initializer and is not definitely assigned in the constructor". Which compiler flag produces that error, and what are the legitimate ways to satisfy it?

level: middleimportance: should knowfreq 42%

answer

  1. fields must be definitely assigned
  2. depends on strictNullChecks
  3. initializer, constructor, or optional type
  4. the analysis stops at the constructor
  5. the bang is a promise, not a check

basics

~20 s

The strictPropertyInitialization flag, enabled by strict and requiring strictNullChecks, produces that error. Satisfy it by giving the field an initializer, assigning it unconditionally in the constructor, or admitting it may be missing by making the type optional or including undefined.

solid answer

~50 s

That is `strictPropertyInitialization`, part of the `strict` family. It requires `strictNullChecks` — the compiler rejects the config if you enable it alone — because without `strictNullChecks` a missing field is already assignable to its declared type and the check would be meaningless. Three answers are honest: give the field an initializer at the declaration; assign it unconditionally in the constructor body; or admit it can be absent by declaring `name?: string` or `name: string | undefined`, which pushes the check to every read. The fourth, `name!: string`, is a definite assignment assertion: it silences the error and verifies nothing, so it belongs only where something outside the constructor genuinely assigns the field, such as a dependency-injection container or an ORM. Note the analysis is constructor-local — assigning in a private helper the constructor calls still errors.

code

typescript · 10 lines
typescript
class User {
  role: string = "member";  // initializer at the declaration
  email: string;            // assigned in the constructor
  nickname?: string;        // optional: the type admits undefined
  id!: string;              // assertion: assigned by a framework, unchecked

  constructor(email: string) {
    this.email = email;
  }
}

go deeper

for a junior

Recognise the error and know the three plain fixes: initialize at the declaration, assign in the constructor, or mark the field optional. Say what the exclamation mark does and why it is not the first choice.

for a middle

Explain the dependency on strictNullChecks and the shape of the analysis: constructor-local, definite rather than conditional, and blind to helper method calls. Show that changing the type to include undefined is a modelling decision, not a workaround.

for a senior

Discuss framework-assigned fields — DI, ORM entities — and how you keep the ! assertion confined to the construction path that needs it, using factories so tests and ordinary code still receive fully-initialized objects.

for a principal

Own the object-construction convention: which types may exist in a partially-initialized state at all, and whether that is allowed to leak past a module boundary. The flag is the compiler's enforcement of a design rule you set.

## The error and the flag The diagnostic is `Property 'name' has no initializer and is not definitely assigned in the constructor. (2564)`, and it comes from `strictPropertyInitialization`, a member of the `strict` family. The problem it addresses is a specific lie: a field declared `name: string` claims never to be `undefined`, yet if nothing ever assigns it, every read gets `undefined` at runtime while the compiler happily allows `this.name.trim()`. ## Why it needs `strictNullChecks` `strictPropertyInitialization` cannot be enabled unless `strictNullChecks` is also on; the compiler reports a configuration error otherwise. The dependency is logical rather than technical. Without `strictNullChecks`, `undefined` is a member of every type, so `name: string` already permits `undefined` and a field that is never assigned tells no lie. The initialization check only has something to enforce once `string` genuinely excludes `undefined`. ## The legitimate answers **Initialize at the declaration.** `name: string = "anonymous";` — the simplest fix, and the right one when a sensible default exists. **Assign in the constructor.** `constructor(name: string) { this.name = name; }` satisfies the check. A parameter property (`constructor(public name: string)`) does the same thing in one line. **Change the type to tell the truth.** If the field really can be absent, say so: `name?: string` or `name: string | undefined`. This is not a workaround; it is the correct model, and its cost is that every read must now narrow. That cost is the information you were previously hiding. **The definite assignment assertion.** `name!: string;` tells the compiler "trust me, this is assigned" and turns the check off for that field with no verification whatsoever. It is the right tool in exactly one situation: a framework outside the constructor assigns the field — a DI container, an ORM hydrating an entity, a test harness. Used anywhere else it is a null-pointer bug postponed. **`declare`.** `declare name: string;` states that the field is provided elsewhere — typically redeclaring a base class member for a narrower type — and emits nothing for that declaration. It is a declaration-only construct, not a general opt-out. ## What the analysis does and does not see The check runs a definite-assignment analysis over the constructor body only, and it is deliberately shallow: ```ts class User { name: string; // still an error constructor() { this.init(); // the compiler does not follow this call } private init() { this.name = "anonymous"; } } ``` The compiler does not follow the call into `init`, because proving assignment through arbitrary method calls (which could be overridden in a subclass, or reassign nothing on some path) is not something it attempts. Conditional assignment fails for the same reason: assigning only inside an `if` branch is not *definite*. Both cases are frequently mistaken for compiler bugs; they are the boundary of the analysis, and the fix is to assign directly in the constructor or to model the field as possibly-undefined. ## Framework-assigned fields The common production case is an entity or component whose fields are populated by something else after `new`. Here `!` is the pragmatic answer, but treat it as a claim you are making on the framework's behalf: if the framework fails to inject, the field is `undefined` and the type system will not warn anyone. Where the object is constructed by your own code too — in tests, in fixtures — prefer a factory function that builds a fully-initialized instance, so the assertion is confined to the one path that genuinely needs it. ## The erasure reminder None of this exists at runtime. `strictPropertyInitialization` emits no checks, no guards and no defaults; a field that is not assigned is simply `undefined` when read. The flag's entire value is that it forces the question at compile time, and every `!` you write is a place where the question was asked and answered with a promise instead of a proof.

  • Why does assigning the field inside a private method called from the constructor still error?
    Because the definite-assignment analysis examines the constructor body and does not follow calls into other methods. Proving assignment through a call would mean reasoning about every path in a method that a subclass could override, so the compiler declines. Assign directly in the constructor, or model the field as possibly-undefined and narrow at the reads.
  • When is the definite assignment assertion on a field actually justified?
    When something outside the constructor reliably assigns it — a dependency-injection container, an ORM hydrating an entity, a test harness. Even then it is an unverified promise: if injection fails, the field is undefined and nothing warns. Confine it to types the framework alone constructs, and use factory functions elsewhere so ordinary code paths still get a fully-initialized object.

saying these in an interview costs you the question

  • Reaches for the ! assertion as the default fix
  • Thinks the compiler follows constructor calls into helper methods
  • Says an assignment inside an if branch satisfies the check
  • Believes the flag inserts a runtime default or guard
  • Enables strictPropertyInitialization without strictNullChecks

context