Constructors
A constructor is the special method that runs when you create an instance with new. In TypeScript it works like the JavaScript constructor but with typed parameters and strict field-initialization checks. Every field a class declares must be assigned a value either at its declaration site or inside the constructor, otherwise the compiler complains under strictPropertyInitialization. This page covers declaring constructors, initializing fields, handling optional and default parameters, and the rarely needed constructor overload syntax.
Declaring a constructor
A constructor uses the reserved name constructor, takes typed parameters, and assigns the class’s fields. You declare the fields with their types and then set them inside the constructor body.
class Point {
x: number;
y: number;
constructor(x: number, y: number) {
this.x = x;
this.y = y;
}
}
const origin = new Point(0, 0); // Point { x: 0, y: 0 }
The constructor never has an explicit return type — it always produces an instance of the class. Calling new Point(0, 0) allocates the object, runs the body, and returns the typed instance.
Strict property initialization
Under strict mode (specifically strictPropertyInitialization), every non-optional field must be definitely assigned. If a field is neither given a default nor set in the constructor, TypeScript reports an error.
class User {
name: string; // ❌ Error: Property 'name' has no initializer
age = 0; // ✅ initialized at declaration
constructor(name: string) {
// forgot to assign this.name
}
}
You can satisfy the checker by assigning in the constructor, providing a default, marking the field optional with ?, or using the definite assignment assertion ! when you know a value arrives elsewhere.
class Config {
endpoint!: string; // definite assignment assertion: "trust me, it's set"
constructor() {
this.load();
}
private load() {
this.endpoint = "https://api.example.com";
}
}
Use the
!definite assignment assertion sparingly. It silences the compiler, so if the field is never actually set you get anundefinedat runtime with no warning.
Optional and default parameters
Constructor parameters follow normal function rules: mark them optional with ? or give them defaults. A default value makes the parameter optional at the call site while keeping the field non-undefined.
class Rectangle {
width: number;
height: number;
constructor(width: number, height = width) {
this.width = width;
this.height = height; // square when height omitted
}
}
new Rectangle(10); // 10 x 10
new Rectangle(10, 5); // 10 x 5
Here height defaults to width, producing a square. Because the default fills in a value, this.height is always a number, never undefined.
Constructor overloads
TypeScript supports constructor overloads: several typed signatures followed by one implementation. This lets a class accept different argument shapes while keeping each call site precisely typed.
class DateRange {
start: Date;
end: Date;
constructor(range: { start: Date; end: Date });
constructor(start: Date, end: Date);
constructor(a: Date | { start: Date; end: Date }, b?: Date) {
if (a instanceof Date) {
this.start = a;
this.end = b!;
} else {
this.start = a.start;
this.end = a.end;
}
}
}
new DateRange(new Date(), new Date());
new DateRange({ start: new Date(), end: new Date() });
Only the overload signatures are visible to callers; the implementation signature must be broad enough to cover them all. Prefer a single flexible signature when possible — overloads add complexity.
When to skip the constructor
If a constructor only copies parameters onto fields, use parameter properties to remove the boilerplate. Adding an access modifier or readonly to a parameter declares and assigns the field automatically.
class Vector {
constructor(
public readonly x: number,
public readonly y: number,
) {}
}
const v = new Vector(3, 4);
console.log(v.x, v.y); // 3 4
This is equivalent to declaring x and y plus assigning them, but far terser.
| Approach | When to use |
|---|---|
| Explicit fields + assignment | Logic beyond simple copying; conditional init |
| Default/optional parameters | Sensible fallbacks; partially-specified construction |
| Constructor overloads | Genuinely distinct argument shapes |
| Parameter properties | Pure “store the argument” constructors |
Best Practices
- Keep constructors lightweight: assign fields and do minimal setup, not heavy I/O.
- Enable
strictsostrictPropertyInitializationcatches uninitialized fields. - Prefer defaults and parameter properties over manual
this.x = xboilerplate. - Avoid throwing from constructors when an exception leaves a half-built object; use a factory method instead.
- Reach for the
!definite assignment assertion only when initialization genuinely happens elsewhere. - Use overloads only for truly distinct shapes; otherwise a union parameter is simpler.
- Make a constructor
privateto force creation through a static factory (e.g. singletons).