Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ConstructorParameters<T> cannot get correct types of overloaded constructor #37079

Closed
meteorlxy opened this issue Feb 27, 2020 · 4 comments
Closed
Labels
Duplicate An existing issue was already created

Comments

@meteorlxy
Copy link

TypeScript Version: 3.8.2

Search Terms: ConstructorParameters, overload

Code

interface Options {
	a: string;
	b: string;
}

class Foo {
	constructor(a: string, b: string);
	constructor(options: Options);
	constructor(options: Options | string, b?: string) {
		// implement
	}
}

// Expected: type Params = [Options] | [string, string]
// Actual: type Params = [Options]
type Params = ConstructorParameters<typeof Foo>;

const paramArray: Params = ['foo', 'bar']; // error
const paramObj: Params = [{ a: 'foo', b: 'bar' }]; // ok

Expected behavior:

type Params = [Options] | [string, string]

Actual behavior:

type Params = [Options]

Playground Link: playground link

Related Issues:

@jcalz
Copy link
Contributor

jcalz commented Feb 27, 2020

This is going to be a design limitation like #26591 and #24275.

You can build something yourself that sort of works, as shown in this SO answer, at least up to some fixed number of overloads. You'd get

type OrderedParams = OverloadedConstructorParameters<typeof Foo>; 
// [[string, string], [Options]]
type Params = OrderedParams[number]; 
// [string, string] | [Options]

const paramArray: Params = ['foo', 'bar']; // okay
const paramObj: Params = [{ a: 'foo', b: 'bar' }]; // okay
implementation details
type ConstructorOverloads<T> =
    T extends {
        new(...args: infer A1): infer R1; new(...args: infer A2): infer R2;
        new(...args: infer A3): infer R3; new(...args: infer A4): infer R4
    } ? [
        new (...args: A1) => R1, new (...args: A2) => R2,
        new (...args: A3) => R3, new (...args: A4) => R4
    ] : T extends {
        new(...args: infer A1): infer R1; new(...args: infer A2): infer R2;
        new(...args: infer A3): infer R3
    } ? [
        new (...args: A1) => R1, new (...args: A2) => R2,
        new (...args: A3) => R3
    ] : T extends {
        new(...args: infer A1): infer R1; new(...args: infer A2): infer R2
    } ? [
        new (...args: A1) => R1, new (...args: A2) => R2
    ] : T extends {
        new(...args: infer A1): infer R1
    } ? [
        new (...args: A1) => R1
    ] : any

type OverloadedConstructorParameters<T> =
    ConstructorOverloads<T> extends infer O ?
    { [K in keyof O]: ConstructorParameters<Extract<O[K], new (...args: any) => any>> } : never

type OverloadedInstanceType<T> =
    ConstructorOverloads<T> extends infer O ?
    { [K in keyof O]: InstanceType<Extract<O[K], new (...args: any) => any>> } : never

Playground link

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Feb 27, 2020
@typescript-bot
Copy link
Collaborator

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@JasonHK
Copy link

JasonHK commented May 6, 2020

This is going to be a design limitation like #26591 and #24275.

You can build something yourself that sort of works, as shown in this SO answer, at least up to some fixed number of overloads. You'd get

type OrderedParams = OverloadedConstructorParameters<typeof Foo>; 
// [[string, string], [Options]]
type Params = OrderedParams[number]; 
// [string, string] | [Options]

const paramArray: Params = ['foo', 'bar']; // okay
const paramObj: Params = [{ a: 'foo', b: 'bar' }]; // okay

implementation details

Playground link

@jcalz That hack has a big flaw. If the overload function or constructor had one or more overload with no arguments, the infer will totally be screwed.

declare function basicFunction(): void;
declare function basicFunction(value: number): void;

type FunctionOverloads<T> =
    T extends {
        (...args: infer A1): infer R1;
        (...args: infer A2): infer R2;
        (...args: infer A3): infer R3;
    } ? [
        (...args: A1) => R1,
        (...args: A2) => R2,
        (...args: A3) => R3,
    ] : T extends {
        (...args: infer A1): infer R1;
        (...args: infer A2): infer R2;
    } ? [
        (...args: A1) => R1,
        (...args: A2) => R2,
    ] : T extends {
        (...args: infer A1): infer R1;
    } ? [
        (...args: A1) => R1,
    ] : never;

type T = FunctionOverloads<typeof basicFunction>;

TypeScript Playground

For some reason T is [(...args: unknown[]) => unknown, () => void, (value: number) => void] but not [() => void, (value: number) => void].

@CMCDragonkai
Copy link

Is there a proper solution for this?

tungs added a commit to tungs/timeweb that referenced this issue May 1, 2023
Removes explicit typing for parameters in the VirtualDate constructor, instead using `ConstructorParameter` of OldDate instead. The current TypeScript implementation for using ConstructorParameters for overloaded constructors is imperfect (see microsoft/TypeScript#37079), but it should be good enough for our purposes.
bigmistqke added a commit to solidjs-community/solid-three that referenced this issue Aug 16, 2023
…nstructorParameters

`ConstructorParameters` does not include overloaded constructors
see microsoft/TypeScript#37079 and pmndrs/react-three-fiber#581
`r3f`s solution is to special-case Color in v8 and solve it later for v9, because they faced some problems with the solution/hack described in the typescript github-issue
The hack didn't seem to cause the same issues with us, so I implemented that instead.

It's a manual union of overloaded methods, going up to 7 overloads, which is probably a bit of overkill.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

6 participants