Typescript infer constructor parameter using conditional types

Viewed 2612

Similarlly to how you can infer function parameters with Typescript via type inference:

type FunctionProps<T> = T extends (arg1: infer U) => any ? U : never;

const stringFunc: (str: string) => string = (str: string): string => str;

type StringFuncType = FunctionProps<typeof stringFunc>;

const a: StringFuncType = 'a';

I want to infer constructor parameters the same way, yet have been unsuccessful so far. Currently my setup looks like this:

type ConstructorProps<T> = T extends {
  new (arg1: infer U): T;
} ? U : never;

class Foo { constructor(a: string) {  } }

type FooConstructor = ConstructorProps<typeof Foo>;

// FooConstructor is always never but should be type string.
const a: FooConstructor = 'a' 

Wasn't sure if this was yet supported in Typescript as the "Advanced Types" section in the TS docs only mention functions and not classes for inference (in regards to parameters).

Anybody else find a solution to this?

4 Answers

If I change the T to any in the return type of the constructor function, the example works:

type ConstructorProps<T> = T extends {
  new (arg1: infer U): any;
//                     ^^^
} ? U : never;

Remember, T is the type of the constructor function, which is not the same as the type of the constructed object.

class Test {
    constructor(foo: number, baz: string) {}
}

type FirstConstructorProp<T> = T extends {
  new (first: infer U, ...rest: any[]): any;
} ? U : never;

type F1 = FirstConstructorProp<Test>; // never
type F2 = FirstConstructorProp<typeof Test>; // number

type ConstructorProps<T> = T extends {
  new (...args: infer U): any;
} ? U : never;

type P1 = ConstructorProps<Test>; // never
type P2 = ConstructorProps<typeof Test>; // [number, string]

It works if you don't use curly brackets, see this other SO.

type ConstructorArgs<T> = T extends new(...args: infer U) => any ? U : never;

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

type Bar = ConstructorArgs<typeof Foo> // type Bar = [string, number]

See associated playground

Related