3

I'm reading TS documentation, when I read More on Functions, I don't understand the code below, how to use it in actual development. Are there any examples?

interface CallOrConstruct {
  new (s: string): Date;
  (n?: number): number;
}
Sebastian Kreft
  • 7,819
  • 3
  • 24
  • 41
OnlyWick
  • 342
  • 2
  • 10

1 Answers1

5

What you see here is a single object that has different behavior depending on whether you call it with new. This is something Javascript is capable of doing, but can lead to confusing code; you'll probably see that kind of signature only very infrequently, or when adapting a very old or flexible Javascript library for Typescript usage.

(Technically, the behavior doesn't have to be different: Sensible objects might be callable with or without new with the same overall behavior, and would require both call and construct signatures. However, this kind of deliberate flexibility is still unusual. Thanks @kaya3!)


Your snippet appears in More on Functions under the text:

Some objects, like JavaScript’s Date object, can be called with or without new. You can combine call and construct signatures in the same type arbitrarily:

This is a reference to the built-in Date object, which has this text on MDN:

Date()

When called as a function, returns a string representation of the current date and time, exactly as new Date().toString() does.

new Date()

When called as a constructor, returns a new Date object.

console.log(typeof Date())      // string
console.log(typeof new Date())  // object

As said above, it is very uncommon that a single Function object will serve both purposes: You'll usually see a function/class deliberately called as one or the other, but not both. However, because it is possible to do in Javascript (and even done in built-in objects) it is important for TypeScript to be able to represent an object that works in both call styles.

interface CallOrConstruct {
  new (s: string): Date;      // construct
  (n?: number): number;       // call
}

// construct
let object: Date = new CallOrConstruct("optional string s");

// call
let myNumber: number = CallOrConstruct(/* n= */ 42);

You probably won't need to ever type out an interface like this, especially if you use class and constructor for classes and function (or other normal function declarations) for your functions. You'll only need to do so if you're trying to declare types TypeScript doesn't understand (as in a .d.ts TypeScript declarations file for an external Javascript library) or if you're trying to write a clever and confusing TypeScript object that can used in both ways. Speaking for myself, I haven't had a reason to write my own declaration like this, though I've read a few in libraries and their typings.

Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
  • OK, Thank you!! In addtion, I also want to ask a question, how this Interface works on functions or classes(I don't know where to put it!) – OnlyWick Mar 30 '21 at 16:51
  • 1
    More commonly (and more sensibly), you can write a constructor in such a way that it can be called with or without `new`, with the same signature either way. In that case it's still necessary to use an interface with both a call signature and a constructor signature. – kaya3 Mar 30 '21 at 16:54
  • @OnlyWick You probably won't need to ever type out an interface like this, especially if you use `class` and `constructor` for classes and `function` (or other normal function declarations) for your functions. You'll only need to type out an interface like this if you're trying to `declare` types TypeScript doesn't understand (as in an external Javascript library) or if you're trying to write a clever and confusing TypeScript object. Speaking for myself, I haven't had a reason to write my own declaration like this, though I've read a few in libraries and their typings. – Jeff Bowman Mar 30 '21 at 16:58
  • @kaya3 This is true, and I can edit in a line about that, but I posit that even the sensible objects that let you call either way are relatively rare and are unlikely to be relevant to a TypeScript/Javascript newcomer. – Jeff Bowman Mar 30 '21 at 17:03
  • @OnlyWick See e.g. [ES6: call class constructor without new keyword](https://stackoverflow.com/questions/30689817/es6-call-class-constructor-without-new-keyword). And yes, this is rare and you absolutely won't need to write any of your own code like this, it's more an issue if you are working with third-party code which does this kind of thing. – kaya3 Mar 30 '21 at 17:04