Take a look at microsoft/TypeScript#3841 for a definitive response to this inquiry. The constructor
property of class instances lacks strong typing in TypeScript. It is simply assigned the type Function
, allowing types like Employee
and Person
to be interchanged without conflict.
However, why isn't p.constructor
more strongly typed? Refer to this comment in microsoft/TypeScript#3841 for explanation.
In TypeScript, it is highly preferable for extended classes to also extend their instances. This allows class Foo extends Bar { ⋯ }
to imply that Foo extends Bar
, ensuring that class hierarchies align with type hierarchies. Without this alignment, code like const bar: Bar = new Foo()
would throw errors, causing major disruption to existing codebases.
JavaScript permits subclass constructors to differ from those of their superclasses, which is necessary given some JavaScript inheritance patterns. Thus, scenarios like the following are allowed:
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
class Employee extends Person {
salary: number;
constructor(name: string, age: number, salary: number) {
super(name, age);
this.salary = salary;
}
}
const pCtor: typeof Person = Employee; // error, incompatible constructors
const p: Person = new Employee("", 0, 0); // no error, compatible instances
This means that p.constructor
cannot be of type typeof Person
without disrupting the mentioned code. While perhaps p.constructor
could possess additional specificity beyond Function
, current functionalities do not reflect this. Nonetheless, this does not impact the provided example scenario.
It's crucial to note that from a type system perspective, your code example mirrors this one identically. Despite differences in the resulting behavior of p instanceof Person
, the structural compatibility remains unchanged. No extends
clause is necessary for types to align. Even if an Employee
class looks like this:
class Employee {
name: string;
age: number;
salary: number;
constructor(name: string, age: number, salary: number) {
this.name = name;
this.age = age;
this.salary = salary;
}
}
there won't be any alterations in TypeScript's behavior. An Employee
's constructor
property maintains a type of Function
, resulting in every Employee
being structurally compatible with Person
.
Visit Playground link to view the code