Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I'm attempting to rewrite my JS into TS. I have a class called Point2D that is used to manipulate 2 dimensional points. I'm getting an error that
Type 'Function' has no construct signatures.ts(2351)
. What am I doing wrong while moving to TS?
class Point2D {
x: number;
y: number;
public constructor(x: number = 0, y: number = 0) {
this.x = x;
this.y = y;
* Translate this point by that point
* @param {Point2D} that
public add(that: Point2D) {
return new this.constructor(this.x + that.x, this.y + that.y); // ERROR
public subtract(that: Point2D) {
return new this.constructor(this.x - that.x, this.y - that.y); // ERROR
* @param {number} scalar
public multiply(scalar:number) {
return new this.constructor(this.x * scalar, this.y * scalar); // ERROR
export default Point2D;
–
The underlying problem with your code is that you can extend a base class with a subclass whose constructor doesn't accept the same argument list as the base class. So new this.constructor(...args) isn't known to be safe for any args. For example:
class StringPoint extends Point2D {
constructor(x: string, y: string) {
super(x.trim().length, y.trim().length);
const s = new StringPoint("abc", "defgh ");
console.log(s.x) // 3
console.log(s.y) // 5
The StringPoint class is derived from Point2D, but its constructor accepts two strings instead of two numbers. And it calls these strings' trim() method. So far everything is fine, but when we call one of your base class methods that uses new this.constructor, things explode at runtime:
console.log(s.multiply(2)) // 💥 ERROR! x.trim is not a function
That's because s.multiply(2) ends up calling new this.constructor(6, 10), but because this is an instance of StringPoint, then this.constructor is StringPoint. So new StringPoint(6, 10) is called, meaning that numbers are passed to something expecting strings, and number doesn't have a trim() method. Oops.
The correct fix here is probably to just use the Point2D constructor directly instead of asking the instance what its constructor was:
public add(that: Point2D) {
return new Point2D(this.x + that.x, this.y + that.y); // okay
public subtract(that: Point2D) {
return new Point2D(this.x - that.x, this.y - that.y); // okay
public multiply(scalar: number) {
return new Point2D(this.x * scalar, this.y * scalar); // okay
That works because Point2D is a specific class constructor known to accept two numbers. My StringPoint example from above now works without a problem, because the base class methods produce instances of the base class and do not even try to construct new instances of potentially incompatible subclasses:
const s = new StringPoint("abc", "defgh ");
console.log(s.x) // 3
console.log(s.y) // 5
console.log(s.multiply(2)) // okay, {x: 6, y: 10}
Here s.multiply(2) is a Point2D but not a StringPoint.
Note that the typings for the constructor property of objects in TypeScript are unfortunately not very useful; this.constructor is seen as being of the Function or "untyped call" type. This is both too strict since there is information you could know about the constructor property which is missing from Function (as discussed in microsoft/TypeScript#3841) and too loose because, although new is rejected, you are allowed to call Function with any arguments at all (as discussed in microsoft/TypeScript#48284).
This means that one might decide that the problem would be fixed if you remove the new operator and just call this.constructor(6, 10) instead of new this.constructor(6, 10). And while that would indeed stop the compiler from issuing an error, the victory would be short-lived because it would guarantee a runtime error from most JavaScript engines. Calling a class constructor without new will give you a TypeError (see the relevant portion of the ECMAScript spec).
So beware when dealing with the constructor property in TypeScript!
Playground link to code
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.