Can you overload a constructor?
1. The general idea of constructor overloading
In TypeScript you can declare several constructor signatures, but implement only one (real) constructor with checks inside it:
class User {
name: string;
age?: number;
// Overloads (type-level only)
constructor(name: string);
constructor(name: string, age: number);
// A single implementation
constructor(name: string, age?: number) {
this.name = name;
this.age = age;
}
}
const u1 = new User("Tim"); // works
const u2 = new User("Alex", 25); // works
const u3 = new User(); // Error: no matching overloadThe
constructor(...)overloads above are only type signatures. The lastconstructor(...)with a body is the real implementation.
2. How this works under the hood
TypeScript compiles everything into a single JavaScript constructor, and the overloads are needed only for type checking at compile time.
// TS
class User {
constructor(name: string);
constructor(name: string, age: number);
constructor(name: string, age?: number) {
console.log(name, age);
}
}Compiles to JS as:
class User {
constructor(name, age) {
console.log(name, age);
}
}The type checks are removed, and a single constructor remains.
3. Checking arguments inside the constructor
Since JS does not support "real" overloading, manual checks are usually done inside the constructor:
class Point {
x: number;
y: number;
constructor(x: number);
constructor(x: number, y: number);
constructor(x: number, y?: number) {
if (y === undefined) {
this.x = this.y = x; // if one argument was passed
} else {
this.x = x;
this.y = y;
}
}
}
new Point(5); // (5, 5)
new Point(5, 10); // (5, 10)4. Overloading with different parameter types
You can overload a constructor by type, not only by the number of arguments:
class FileData {
path: string;
buffer?: Uint8Array;
constructor(path: string);
constructor(buffer: Uint8Array);
constructor(value: string | Uint8Array) {
if (typeof value === "string") {
this.path = value;
} else {
this.buffer = value;
this.path = "memory.bin";
}
}
}
new FileData("file.txt"); // string
new FileData(new Uint8Array([1, 2])); // Uint8Array
new FileData(123); // A type error5. Overloading via union types (an alternative)
Sometimes overloads can simply be replaced with a union of types (|):
class User {
constructor(public name: string | number) {}
}
new User("Tim");
new User(123);But then TS does not distinguish the signatures, and the types must be resolved manually inside the constructor.
6. Constructor overloading in a base and a derived class
You can overload a constructor in a parent class and call the right one via super():
class Shape {
color: string;
constructor();
constructor(color: string);
constructor(color = "black") {
this.color = color;
}
}
class Circle extends Shape {
radius: number;
constructor(radius: number);
constructor(radius: number, color: string);
constructor(radius: number, color?: string) {
super(color);
this.radius = radius;
}
}
const c1 = new Circle(10);
const c2 = new Circle(10, "red");7. An important nuance: overloads are not runtime overloading
All the signatures are checked only by the compiler and do not exist in the compiled code:
- TypeScript checks the correct number and types of arguments.
- JavaScript executes a single constructor with checks inside it.
- You cannot have two implementations of a constructor.
Summary
| Capability | Supported | Comment |
|---|---|---|
| Several constructor signatures | Yes | Checked by the compiler |
| Several constructor implementations | No | Only one, with a body |
| Overloading by number of arguments | Yes | Via ? or checks |
| Overloading by type | Yes | Via unions or signatures |
| Real runtime overloading (as in C#/Java) | No | Not possible |
Type checking at new | Yes | Works thanks to the signatures |
In short:
In TypeScript you can overload constructors, but only at the type level. The implementation is always a single one, and you check the arguments yourself inside it.
Short Answer
Interview readyA concise answer to help you respond confidently on this topic during an interview.