piotrwitek / utility-types

Collection of utility types, complementing TypeScript built-in mapped types and aliases (think "lodash" for static types).
MIT License
5.54k stars 230 forks source link

Add new type: `AbstractConstructorParameters<T>` #160

Open hackape opened 3 years ago

hackape commented 3 years ago

Is your feature request related to a real problem or use-case?

Utility type to get constructor params from abstract class. TS built-in ConstructorParameters does not work on abstract class type since it's not constructible.

abstract class AbstractBase {
  constructor(foo: string, bar: number) {}
}

class Child extends AbstractBase {
  constructor(...args: ConstructorParameters<typeof AbstractBase>) {
    super(...args)
  }
}

/**
 * Error:
 * Type 'typeof AbstractBase' does not satisfy the constraint 'new (...args: any) => any'.
 * Cannot assign an abstract constructor type to a non-abstract constructor type.ts(2344)
 */

Describe a solution including usage in code example

Solution:

type AbstractConstructorParameters<T> = ConstructorParameters<(new (...args: any) => any) & T>

Usage:

abstract class AbstractBase {
  constructor(foo: string, bar: number) {}
}

class Child extends AbstractBase {
  // now it works!
  constructor(...args: AbstractConstructorParameters<typeof AbstractBase>) {
    super(...args)
  }
}

Who does this impact? Who is this for?

Typescript user, generally useful in OOP.

Credit

Found this workaround here, which in turn refers to this reddit post by u/adamuso

CMCDragonkai commented 3 years ago

This doesn't work for object destructured assignment:

type AbstractConstructorParameters<T> = ConstructorParameters<(new (...args: any) => any) & T>;

abstract class Test {
    constructor ({
        a,
        b
    }: {
        a: string,
        b: string
    }) {
        console.log(a);
    }
}

class Test2 extends Test {
    constructor (
        {
            c: string,
            ...rest
        }: {
            c: string;
            rest: AbstractConstructorParameters<typeof Test>
        }
    ) {
        super(...rest);
    }

}