DEV Community

Cover image for Fix TS2564: Property Has No Initializer in TypeScript
Mahdi BEN RHOUMA
Mahdi BEN RHOUMA

Posted on • Originally published at iloveblogs.blog

Fix TS2564: Property Has No Initializer in TypeScript

You enable strict (or upgrade a project that just turned it on) and TypeScript stops the build:

Property 'name' has no initializer and is not definitely assigned
in the constructor. (2564)
Enter fullscreen mode Exit fullscreen mode

This is TS2564, produced by the strictPropertyInitialization compiler flag. It is not noise — it is TypeScript refusing to let a class field be silently undefined. The right fix depends on why the field is unset, and choosing wrong trades a compile error for a runtime bug.

{ name: "TypeScript", version: "2.7+" },
{ name: "strict", version: "enabled" },
]} />

What triggers it

strictPropertyInitialization (introduced in TypeScript 2.7, and part of the strict family) checks that "each instance property of a class gets initialized in the constructor body, or by a property initializer." If neither happens and the type is not optional, you get TS2564.

Because it ships inside strict, flipping "strict": true can surface TS2564 in code that compiled fine before — the 2.7 release notes warn about exactly this.

Fix 1 — Initialize at declaration

If the field has a sensible default, set it inline:

class Account {
  accountType = "user";   // OK — initialized by a property initializer
  retries = 0;
}
Enter fullscreen mode Exit fullscreen mode

Fix 2 — Initialize in the constructor

class GoodGreeter {
  name: string;
  constructor() {
    this.name = "hello";   // OK — assigned in the constructor body
  }
}
Enter fullscreen mode Exit fullscreen mode

This is the #1 confusion with TS2564. From the TypeScript Classes handbook: the field must be initialized in the constructor itself. TypeScript does not analyze methods you invoke from the constructor, "because a derived class might override those methods and fail to initialize the members."

class Bad {
  name: string;            // TS2564 — still flagged
  constructor() { this.init(); }
  init() { this.name = "x"; }   // not analyzed
}
Enter fullscreen mode Exit fullscreen mode

Fix 3 — Definite assignment assertion !

When something outside the constructor reliably assigns the property — a DI container, a framework, a lifecycle hook — tell the compiler with !:

class Service {
  private repo!: Repository;   // "!" = definite assignment assertion
  constructor() { this.wire(); }
  wire() { this.repo = makeRepo(); }
}
Enter fullscreen mode Exit fullscreen mode

The TypeScript 2.7 notes describe ! as relaying "that a variable is indeed assigned for all intents and purposes, even if TypeScript's analyses cannot detect so" — and cite dependency injection as the canonical reason it exists.

wrong="profile!: UserProfile // slapped on just to make the red squiggle disappear"
right="profile?: UserProfile // honest: it may be undefined until loaded — now the compiler forces you to check"
/>

The risk: ! is unchecked. If the property is never actually assigned, you get a silent runtime undefined and zero compile-time warning. Use it only when assignment is genuinely guaranteed.

Fix 4 — Mark it optional or union with undefined

If the property may legitimately be absent, say so — and let the compiler force you to handle the undefined case:

class Form {
  email?: string;                // optional
  address: string | undefined;   // explicit union, equivalent intent
}
Enter fullscreen mode Exit fullscreen mode

The handbook's guidance: "if we truly meant for [it] to potentially be undefined, we should have declared it" that way.

Fix 5 — Constructor parameter properties

The public/private/protected/readonly prefix on a constructor parameter declares and assigns the field in one step, so it satisfies the check with no body:

class Point {
  constructor(public readonly x: number, private y: number) {}
  // x and y are declared and assigned — no TS2564
}
Enter fullscreen mode Exit fullscreen mode

Framework note: Angular @Input

Angular treats @Input properties as optional by design. The idiomatic options are a default value, ?, or — for @Input({ required: true }) — the definite assignment assertion:

@Input() id = 'default_id';     // default
@Input() movie?: Movie;         // optional
@Input({ required: true }) data!: Data;   // required + definite assignment
Enter fullscreen mode Exit fullscreen mode

Last resort — disable the flag

You can turn it off, but it removes the guarantee for every class in the project:

// tsconfig.json
{ "compilerOptions": { "strictPropertyInitialization": false } }
Enter fullscreen mode Exit fullscreen mode


Disabling it reintroduces the "uninitialized field is silently undefined" bug class everywhere — strictly worse than a targeted ! or ? on the one property that needs it.

  • You're on TypeScript before 2.7 — the flag and the prop!: T syntax don't exist there.
  • A commonly-repeated claim is that this check only applies when strictNullChecks is also on. The official pages I cite don't state that dependency — don't rely on it without verifying against your own compiler version.

Which fix to use

Situation Fix
There's a sensible default Initialize at declaration
Always set during construction Assign in the constructor body
Assigned by DI / framework / lifecycle Definite assignment prop!: T
May genuinely be missing Optional prop?: T
Passed into the constructor Parameter property

Official references: strictPropertyInitialization, TypeScript 2.7 release notes, Classes handbook.

Related Articles

Frequently Asked Questions

What does "has no initializer and is not definitely assigned" mean?

It is TS2564 from strictPropertyInitialization. You declared a non-optional class property, but TypeScript can't prove it gets a value before use. Give it a default, assign it in the constructor, or mark it ! or ?.

Why does assigning the property in a method not fix TS2564?

TypeScript only analyzes the constructor body, not methods you call from it — a subclass could override that method and skip the assignment. Assign in the constructor directly, or use prop!: T if a framework truly sets it.

Is the definite assignment assertion (!) safe?

Only when assignment is actually guaranteed (e.g. by DI). It's unchecked: if the property is never assigned, you get a silent runtime undefined. Use it deliberately.


Originally published at https://www.iloveblogs.blog

Top comments (0)