In TypeScript you can describe a constructor in an interface, using the new
keyword:
interface Point
{
new(x: number, y: number);
x: number;
y: number;
}
(Marked red because it is not possible that way, although syntactically correct!)
The TypeScript language specification says about this
An object type containing one or more construct signatures is said to be a constructor type.
and
Constructor types may be written using constructor type literals or by including construct signatures in object type literals.
Lots of new terms, further explanations missing. Constructors can not be described in Java interfaces, such a TypeScript feature would be something completely new.
Considering this, a question comes to my mind: What if you receive a JSON object from an AJAX call, cast it to an interface that holds a constructor signature, could you then construct a new object of that type, maybe even without having loaded the definition of that class? I can't believe this. But when no, what sense does it make to give a constructor signature in an interface?
On stackoverflow I found this:
Construct signatures in interfaces are not implementable in classes; they're only for defining existing JS APIs that define a 'new'-able function.
So it looks like you can't use them in interfaces to describe the constructor of the implementing class. This syntax exists just to describe JavaScript built-in types like Date
or RegExp
.
The construct signature
new <RETURNTYPE>(...constructorParameters: any): RETURNTYPE
would look like the following in an example for the type Point
new (x: number, y: number): Point
The constructor type literal
new <RETURNTYPE>(...constructorParameters: any) => RETURNTYPE
is equivalent to the object type literal
{
new <RETURNTYPE>(...constructorParameters: any): RETURNTYPE;
}
Thus following example describing the constructor of type Point
new (x: number, y: number) => Point
is equivalent to
{
new (x: number, y: number): Point;
}
Compiling the following TypeScript code
you would receive the compile error
ConstructableInterface.ts(9,7): error TS2420: Class 'PointImpl' incorrectly implements interface 'Point'.
Type 'PointImpl' provides no match for the signature 'new (x: number, y: number): any'.
ConstructableInterface.ts(20,7): error TS2322: Type 'PointImpl' is not assignable to type 'Point'.
Type 'PointImpl' provides no match for the signature 'new (x: number, y: number): any'.
Mind that the compiler reports any
as return-type of the construct signature, so it not even infers that the result would be the type itself! The TS Handbook justification is:
This is because when a class implements an interface, only the instance side of the class is checked. Since the constructor sits in the static side, it is not included in this check.
To use a construct signature in an interface you must implement it like the following:
You must separate the constructor description from the type description (interface) and put it into its own interface. The class PointImpl
must not to implement PointConstructor
, this is implicitly checked by the compiler when calling newPoint()
. In case you implement it, you will see this compile error:
FactoryFunction.ts(12,7): error TS2420: Class 'PointImpl' incorrectly implements interface 'PointConstructor'.
Type 'PointImpl' provides no match for the signature 'new (x: number, y: number): Point'.
The interface PointConstructor
seems to be the constructor type. Finally, in the line const point: Point = newPoint(PointImpl, 1, 2)
, the constructor turns out to be the name of the implementing class, a little bit unexpected.
Currently I can not find any practical sense in this, because instead of passing the type to the factory I could also construct an object of that type directly via new
. But maybe I haven't had to integrate JS implementations into TS code yet.
ɔ⃝ Fritz Ritzberger, 2018-06-10