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.