0

I want to create a function that can be invoked with the new keyword. However, none of my implementation works and Typescript keeps showing me errors.

Below is my code. You can see it on the Typescript Playground too

type Account = {
  username: string
  password: string
}

type AccountConstructor = {
  new (username: string, password: string): Account
  (username: string, password: string): Account
}

// const Account: AccountConstructor = function (username: string, password: string) {
//   return {username, password}
// }

// Type '(username: string, password: string) => { username: string; password: string; }' is not assignable to type 'AccountConstructor'.
// Type '(username: string, password: string) => { username: string; password: string; }' provides no match for the signature 'new (username: string, password: string): Account'.
const Account: AccountConstructor = function(username: string, password: string) {
  return {
    username,
    password
  }
}

// Type 'typeof Account' is not assignable to type 'AccountConstructor'.
// Type 'typeof Account' provides no match for the signature '(username: string, password: string): Account'.
const Account: AccountConstructor = class {
  constructor(public username: string, public password: string) {}
}

new Account('username', 'password')

How to implement the Construct Signature correctly that the function could be invoked using the new keyword?

Ngọc Nguyễn
  • 339
  • 4
  • 16
  • How is the class approach not working? could you elaborate what error do you see when doing " new AccountClass ('username', 'password') " ? also i can't see you doing that in the code above. – root Oct 16 '22 at 04:56
  • 1
    Why not just write `class Account { ... }`? – kaya3 Oct 16 '22 at 06:53
  • @root My mistake. The `AccountClass` class is created just to test if the normal class works fine. It does not relate to the question. I have updated the code, so we can focus on the errors thrown by the Construct Signature, which is the `AccountConstructor` type – Ngọc Nguyễn Oct 16 '22 at 06:53
  • @kaya3 `class Account { ... }` is okay but I want to use the `new` keyword to invoke a function just like I can do with JavaScript. I discovered construct signature, but don't know how to use it. The official doc doesn't show how to implement it too. – Ngọc Nguyễn Oct 16 '22 at 06:56
  • 1
    The official documentation doesn't show how to do it because you aren't supposed to do it - that is the old way of creating "classes", from before Javascript had the `class` syntax. – kaya3 Oct 16 '22 at 06:57
  • @kaya3 So what is the point of Construct Signature? Typescript invented it for nothing? – Ngọc Nguyễn Oct 16 '22 at 07:06
  • See [here](https://stackoverflow.com/a/13408029/12299000): *"Construct signatures in interfaces are not implementable in classes; they're only for defining existing JS APIs that define a 'new'-able function."* So you would use them in a `.d.ts` file for a library that is not written in Typescript. (You can also use construct signatures directly to pass classes around and instantiate them dynamically. But your interface won't work for this because it also has a call signature, and classes don't have call signatures.) – kaya3 Oct 16 '22 at 07:09
  • @kaya3 Thank you. It makes sense now. Using `class` is the only possible way to create a new object using the `new` keyword. – Ngọc Nguyễn Oct 16 '22 at 07:16
  • @kaya3 Could you post it as an answer so I can give you a tick? – Ngọc Nguyễn Oct 16 '22 at 07:18

1 Answers1

3

In Typescript code you should write classes, not constructor functions; constructor functions are the old way of implementing "classes" in Javascript, from before the class syntax was added. This pattern may still be used in older libraries, or libraries compiled to ES5 or earlier for compatibility purposes. (If you need this compatibility, set your target to ES5, and your Typescript class declarations will be automatically converted when you compile.)

Constructor signatures are primarily meant to be used when defining types in a .d.ts file for a library that is not written in Typescript. However, you can also use them for passing classes around like this:

type Foo = {x: string}

type FooConstructor = new (x: string) => Foo

class FooClass {
    constructor(public x: string) {}
}

const fooConstructor: FooConstructor = FooClass;

Playground Link

The difference between this and your example using a class, is that your interface requires both a construct signature and a call signature, but classes do not have call signatures.

kaya3
  • 47,440
  • 4
  • 68
  • 97