1

I'm trying to create a function that triggers the class's init method and returns the instantiated class, but I'm not able to preserve the constructor and class types.

I tried:

class Test {
  constructor(a: number, b: string, c?: number[]) {}

  protected init() {}
}

export function instantiateWithInit<Type extends new (args: any) => { init: () => any }>(
  clazz: new (args: ConstructorParameters<Type>) => InstanceType<Type>,
  ...args: ConstructorParameters<Type>
) {
  const instance = new clazz(args);
  instance.init();
  return instance;
}

instantiateWithInit(Test, "");

But the type return only returns the init method and I also have the error of the parameters not matching the type constructor:

Argument of type 'typeof Test' is not assignable to parameter of type 'new (args: [args: any]) => { init: () => any; }'.
  Types of construct signatures are incompatible.
    Type 'new (a: number, b: string, c?: number[]) => Test' is not assignable to type 'new (args: [args: any]) => { init: () => any; }'.ts(2345)
Sanket Shah
  • 2,888
  • 1
  • 11
  • 22
Slinidy
  • 367
  • 1
  • 4
  • 12

1 Answers1

0

One of the problems here is that your function expects the constructor to only accept one argument. Using the spread operator, this perticular error can be fixed.

To actually get your idea working, I had to rewrite the function definition like this:


class Test {
    constructor(a: number, b: string, c?: number[]) { }

    public init() { }
}

export function instantiateWithInit<
    T extends { init(): any },
    A extends any[]
>(
    clazz: new (...args: A) => T,
    ...args: A
) {
    const instance = new clazz(...args);
    instance.init();
    return instance;
}

const abc = instantiateWithInit(Test, 5, "");
// ^ abc is of type Test
// Also the arguments are checked (hence I needed to add that 5)

I have to admit, TypeScript can be tricky when it comes to extends in function definitions.

Kelvin Schoofs
  • 8,323
  • 1
  • 12
  • 31