Reputation: 1359
Imagine we have an interface which defines what arguments should constructor accept:
interface Ctr {
new (num: number): any;
}
And then we have a class with default constructor:
class DefCtr {
}
Then, for some reason, I can assign DefCtr
class to a variable of type Ctr
:
const Instance: Ctr = DefCtr;
const i = new Instance(1);
console.log(i);
// The output is:
// DefCtr {}
So the Instance
is a DefCtr
class (which has no constructor accepting number). But interface is forcing me to pass a number (which will be ignored) when creating an instance of it.
What's interesting this works only when class has the default constructor. For example, this won't work:
interface Ctr {
new (num: number): any;
}
class NotDefCtr {
// Lets create a non-default constructor
constructor(str: string) {
}
}
const Instance: Ctr = NotDefCtr; // Error:
// Type 'typeof NotDefCtr' is not assignable to type 'Ctr'.
// Types of parameters 'str' and 'num' are incompatible.
// Type 'number' is not assignable to type 'string'.
Error makes sense. But from my point of view, it would also make sense to receive an error when trying to assign a class with default constructor (since it doesn't have any other constructor matching interface).
Upvotes: 2
Views: 67
Reputation: 329453
The TypeScript FAQ has an entry: Why are functions with fewer parameters assignable to functions that take more parameters?. Also see the handbook documentation for function type compatibility. This is the same issue except with a newable instead of a callable. I'll translate that entry to this case:
This is the expected and desired behavior. There is a principle of substitutability that says that if an object X
can be used in place of some object Y
, then X
is a subtype of or assignable to Y
.
In this case, DefCtr
is a subtype of Ctr
because DefCtr
's constructor can safely ignore extra number
parameter. But NotDefCtr
is not a subtype of Ctr
because NotDefCtr
's type indicates that it expects an argument of type string
; a number
cannot safely be used as a string
, so it's not compatible.
(Aside: it turns out that NotDefCtr
's constructor actually ignores its input, so at runtime no explosion would happen. But the type system does not care about the particular implementation of the constructor; it sees the signature a contract saying that NotDefCtr
's constructor is within its rights to expect an argument of type string
. So it's still an error to substitute a Ctr
with NotDefCtr
.)
Upvotes: 3