9

I tried with this but it doesn't work. Foo is just a test of what works. Bar is the real try, it should receive any newable type but subclasses of Object isn't valid for that purpose.

class A {

}
class B {
    public Foo(newable: typeof A):void {

    }
    public Bar(newable: typeof Object):void {

    }
}

var b = new B();
b.Foo(A);
b.Bar(A); // <- error here
Áxel Costas Pena
  • 5,886
  • 6
  • 28
  • 59

3 Answers3

16

You can use { new(...args: any[]): any; } to allow any object with a constructor with any arguments.

class A {

}

class B {
    public Foo(newable: typeof A):void {

    }

    public Bar(newable: { new(...args: any[]): any; }):void {

    }
}

var b = new B();
b.Foo(A);
b.Bar(A);  // no error
b.Bar({}); // error
David Sherret
  • 101,669
  • 28
  • 188
  • 178
1

If you want to enforce only certain newables, you can specify the constructor's return type

interface Newable {
  errorConstructor: new(...args: any) => Error; // <- put here whatever Base Class you want
}

equivalent

declare class AnyError extends Error { // <- put here whatever Base Class you want
  // constructor(...args: any) // you can reuse or override Base Class' contructor signature
}

interface Newable {
  errorConstructor: typeof AnyError;
}

testing

class NotError {}
class MyError extends Error {}

const errorCreator1: Newable = {
  errorConstructor: NotError, // Type 'typeof NotError' is missing the following properties from type 'typeof AnyError': captureStackTrace, stackTraceLimitts
};

const errorCreator2: Newable = {
  errorConstructor: MyError, // OK
};
Qwerty
  • 29,062
  • 22
  • 108
  • 136
  • 2
    Note that this approach of `AnyError extends Error` is very specific to this case, because `Error` is not typed as Class in the internals and therefor cannot be simply used as `typeof Error` on the right side. – Qwerty Oct 31 '19 at 13:49
0

Using the Construct Signature feature of TypeScript, you can create a newable function.

/*
* Create a newable function
*/

type Vector2D = {
  x: number
  y: number
}

type Vector2DConstructor = {
  new(x: number, y: number): Vector2D
  (x:number, y: number): Vector2D
}

const Vector2D = function (this: Vector2D, x: number, y: number): Vector2D {
  if (x < 3) throw new Error('x cannot be smaller than 3')
  if (!new.target) {
    return {x, y}
  }
  this.x = x
  this.y = y
  return this
} as Vector2DConstructor // without type casting, it won't work

const a = new Vector2D(3, 3)
console.log(a)

You can try it on the playground.

There are some drawbacks to this:

  • You have to use type-cast, which is not safe for error checking.
  • You have to make sure the parameters of the Constructor type match the parameters of the function. Otherwise, it will create unexpected errors.
Ngọc Nguyễn
  • 339
  • 4
  • 16