TypeScript Constructor Overload with Empty Constructor
Solution 1
Because your constructor implementation is called by all your overload constructors. (Technically, at runtime there's only one constructor function that gets called with the various overload argument signatures.)
Imagine it like this:
overload_constructor(id:string) {
implementation_constructor(id);
}
implementation_constructor(id:string, name?:string, age?:number) {
// ...
}
Thinking of it this way, overload_constructor
could not call implementation_constructor
unless name
and age
are optional.
Also see Basarat's answer, the implementation isn't exposed for public usage by the type checker (though at runtime it's the "real" constructor used in JS). If you want to only allow ()
, (id)
, or (id, name, surname, email)
as the only valid call signatures you would do it like this:
constructor()
constructor(id: number)
constructor(id: number, name: string, surname: string, email: string)
constructor(id?: number, name?: string, surname?: string, email?: string) {
this.id = id;
this.name = name;
this.surname = surname;
this.email = email;
}
Note that in the implementation all parameters are optional, but that signature is not exposed when compiling and you can only use these these calls:
new Foo()
new Foo(1)
new Foo(1, "a", "b", "c")
Not, for example:
new Foo(1, "a")
Solution 2
The last function overload is only used in the implementation and not available publicly. This is shown below:
class Foo{
constructor()
constructor(id?: number) {
}
}
const foo1 = new Foo();
const foo2 = new Foo(123); // Error! : not public
If you want id:number
to be available publically ofcourse you can add another overload:
class Foo{
constructor()
constructor(id: number)
constructor(id?: number) {
}
}
const foo1 = new Foo();
const foo2 = new Foo(123); // Okay
const foo3 = new Foo('hello'); // Error: Does not match any public overload
The reason is that TypeScript tries not to do fancy code generation for function overloading (traditional languages do this using name mangling e.g. C++)
So you can pass none parameter or must pass parameters.
Actually you can make the final overload optional but none of the public ones as optional. Consider the following example:
class Foo{
constructor(id: number, name:string)
constructor(name:string)
constructor(idOrName?: number|string, name?:string) {
}
}
const foo1 = new Foo('name'); // Okay
const foo2 = new Foo(123); // Error: you must provide a name if you use the id overload
const foo3 = new Foo(123,'name'); // Okay
Solution 3
You can use Builder pattern to solve this. Even in C# or Python, it quickly becomes a better approach as the number of constructor arguments grows.
class Foo {
constructor(public id: number, public name: string, public surname: string, public email: string) {
}
static Builder = class {
id: number = NaN;
name: string = null;
surname: string = null;
email: string = null;
Builder() {
}
build(): Foo {
return new Foo(this.id, this.name, this.surname, this.email);
}
}
}
Daniel Hitzel
Updated on March 27, 2020Comments
-
Daniel Hitzel about 4 years
Why is it not allowed to have separate
constructor
definitions in TypeScript?
To have e.g. twoconstructors
, I need to write my code like this.constructor(id: number) constructor(id: number, name?: string, surname?: string, email?: string) { this.id = id; this.name = name; this.surname = surname; this.email = email; }
Thereby I need to put
?
after the parameters that are not required in the first constructor.Why can't I write it like this?
constructor(id: number) { this.id = id; } constructor(id: number, name: string, surname: string, email: string) { this.id = id; this.name = name; this.surname = surname; this.email = email; }
So that for both constructors all parameters are mandatory.
Moreover, if I need to have an empty constructor things get even weirder, since I need to mark every parameter with a
?
.constructor() constructor(id?: number, name?: string, surname?: string, email?: string) { this.id = id; this.name = name; this.surname = surname; this.email = email; }
Why does TypeScript differs from common languages like
C#
orPython
here?I would expect it to work like this.
constructor() { } constructor(id: number, name: string, surname: string, email: string) { this.id = id; this.name = name; this.surname = surname; this.email = email; }
So you can pass none parameter or must pass all parameters.
-
Daniel Hitzel about 8 yearsThat makes sense. Do you know if this is a limitation to JavaScript (ES5) to which TypeScript get transpilt or it is a design decision of TypeScript developers?
-
Aaron Beall about 8 yearsWell, ES5 doesn't have classes at all, only constructor functions. You can't have multiple constructor functions represent a single object type. In ES6 we have classes but only a single constructor function. In JS all function parameters are optional all the time, and untyped. This is why we use TypeScript. :-)
-
Daniel Hitzel about 8 yearsSo why is there only one constructor function in TypeScript (at runtime)? I don't get the benefit :D
-
Aaron Beall about 8 yearsThere's only one constructor function in TypeScript at runtime because it's transpiled to JS for runtime, and JS doesn't support multiple constructors in any way. The constructor overloads are just for the TypeScript compiler for better static typing.
-
Daniel Hitzel about 7 yearscan you then overload the static method?