Behemoth
Behemoth

Reputation: 9310

How can I add function typings for static method chaning in TypeScript?

Image I have this little class with 2 static methods. Like this I can chain the methods as I want.

class MyClass {
  static x() {
    console.log("x");
    return this;
  }

  static y() {
    console.log("y");
    return this;
  }
}

MyClass.x().y();

If I wanted to add typings for the 2 methods how would the typings look like? In other words how can I define the correct return type?

class MyClass {
  static x(): MyClass {
    console.log("x");
    return this;
  }

  static y(): MyClass {
    console.log("y");
    return this;
  }
}

MyClass.x().y();

This doesn't work but hopefully shows what I mean.

Upvotes: 1

Views: 873

Answers (1)

jcalz
jcalz

Reputation: 327849

The type named MyClass corresponds to an instance of the MyClass class, and as such does not have the static methods such as x or y.

Inside a static method implementation, this refers to the constructor itself, which (as long as you don't have subclasses to worry about) will be the value named MyClass. The type of the MyClass constuctor is not MyClass, but typeof MyClass, using the Typescript typeof type query operator.

If you use IntelliSense to inspect the return types inferred for x() and y() in your example code, you'll see:

class MyClass {       
  // (method) MyClass.x(): typeof MyClass
  //     ↓
  static x() {
    console.log("x");
    return this;
  }
}

If you really want to annotate the type for some reason, typeof MyClass is potentially the right answer:

class MyClass {
  static x(): typeof MyClass {
    console.log("x");
    return this;
  }

  static y(): typeof MyClass {
    console.log("y");
    return this;
  }
}

Note that things get a bit trickier if you have subclasses and you want to capture the fact that subclasses have inheritance on the static side too:

class YourClass extends MyClass {
  static z = 123;
}

YourClass.x().z // error!
// ---------> ~
// not known to exist by TypeScript but exists at runtime

You probably don't care about that, but if you do it's an outstanding issue in TypeScript; see microsoft/TypeScript#5863 for discussion and workarounds. More information available upon request.


Playground link to code

Upvotes: 2

Related Questions