添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
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;
                this.constructor isn't known to be type safe, since subclasses might have constructors that accept completely different arguments, as shown in this playground link.  Why not just use Point2D instead of this.constructor, as shown in this playground link?  Then it's type safe and the compiler is happy with it.  Does that fully address your question? If so I'll write up an answer explaining fully; if not, what am I missing?
– jcalz
                Jan 29 at 18:00

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.