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

Function argument inference only handles one overload #26591

Closed
JakeTunaley opened this issue Aug 22, 2018 · 7 comments
Closed

Function argument inference only handles one overload #26591

JakeTunaley opened this issue Aug 22, 2018 · 7 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@JakeTunaley
Copy link

TypeScript Version: 3.1.0-dev.20180821

Search Terms: function argument infer overload return type rest tuple

Code

function foo (a: string): string;
function foo (a: number, b: string): boolean;
function foo (a: boolean, ...b: object[]): object;
function foo (...args: any[]): any {
}

type A = ReturnType<typeof foo>;

Expected behavior:

Type A is a union of the return types of every overload, or an error along the lines of "cannot infer things about an overloaded function" is generated.

Actual behavior:

Type A is object.

This also happens when inferring function params:

type Parameters<T extends (...args: any[]) => any> = T extends (...args: infer U) => any ? U : never;

type B = Parameters<typeof foo>; // [boolean, ...object[]]

Playground Link:

http://www.typescriptlang.org/play/#src=function%20foo%20(a%3A%20string)%3A%20string%3B%0D%0Afunction%20foo%20(a%3A%20number%2C%20b%3A%20string)%3A%20boolean%3B%0D%0Afunction%20foo%20(a%3A%20boolean%2C%20...args%3A%20object%5B%5D)%3A%20object%3B%0D%0Afunction%20foo%20(...args%3A%20any%5B%5D)%3A%20any%20%7B%0D%0A%7D%0D%0A%0D%0Atype%20A%20%3D%20ReturnType%3Ctypeof%20foo%3E%3B%0D%0A%0D%0Atype%20Parameters%3CT%20extends%20(...args%3A%20any%5B%5D)%20%3D%3E%20any%3E%20%3D%20T%20extends%20(...args%3A%20infer%20U)%20%3D%3E%20any%20%3F%20U%20%3A%20never%3B%0D%0A%0D%0Atype%20B%20%3D%20Parameters%3Ctypeof%20foo%3E%3B%20%2F%2F%20%5Bboolean%2C%20...object%5B%5D%5D

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Aug 22, 2018
@RyanCavanaugh
Copy link
Member

See comments in #21847 - overload resolution in conditional types is not supported yet

@bdpartridge
Copy link

I also encountered this issue while working with a partially-applied version of a hyperscript-style function. I was able to work around it by specifying the last overload of the Hyperscript function type to be a less specific catch-all.

declare function partial<Arg1, Args extends any[], Ret>(
  func: (arg1: Arg1, ...args: Args) => Ret,
  arg1: Arg1,
): (...args: Args) => Ret;

interface Hyperscript {
  (selector: string, ...children: Children[]): Vnode;
  (selector: string, attrs: Attributes, ...children: Children[]): Vnode;
  (selector: string, ...args: [Attributes, ...Children[]] | Children[]): Vnode;
}

type Attributes = Record<string, any>;

type Children = Vnode | string | number | boolean | null | undefined;

interface Vnode {
  tag: string;
  // other props omitted for brevity
}

declare const h: Hyperscript;

// plain hyperscript function
h("div", { id: "foo" }, "foo", h("div", { id: "bar" }, "bar"));
h("div", "foo", "bar");

// partially-applied hyperscript function
const div = partial(h, "div");
div({ id: "foo" }, "foo", div({ id: "bar" }, "bar"));
div("foo", "bar");

This may not help in all cases where this is an issue, but I thought I'd share just in case others might find this useful.

@ggoodman
Copy link

I was hoping to leverage the existing typing on known event names in common node EventEmitters with the incoming addition of once(ee: EventEmitter, event: string): Promise<any[]> to the core library.

I took the the following approach in the playground and that led me to adding in my use-case to this issue.

interface EE {
  on(event: string, handler: () => void): void;
}

interface Request extends EE {
  on(foo: 'error', handler: (error: Error) => void): void;
  on(foo: 'data', handler: (data: string) => void): void;
}

type HandlerForEvent<T extends EE, E extends string> = T extends { on: (event: E, handler: infer H) => void } ? H : never;

type ErrorHandler = HandlerForEvent<Request, 'error'>;
type ErrorHandlerArgs = Parameters<ErrorHandler>;
// Types as never

type DataHandler = HandlerForEvent<Request, 'data'>;
type DataHandlerArgs = Parameters<DataHandler>;
// Types as [string] !!

If you reverse the order of the Request#on overloads, the never will swap.

It would be neat if the arguments for overloaded methods were treated as a union of tuples according to the overloads.

@gogoyqj
Copy link

gogoyqj commented Apr 2, 2019

well, i have an idea

type GetArgumentsType<Delay> =
    | (Delay extends (one: infer ONE) => any ? [ONE] : never)
    | (Delay extends (...args: infer MANY) => any ? MANY : never);

this worked for me in some case for infer arguments

@buschtoens
Copy link

@RyanCavanaugh Is this closed as "won't fix" or was the issue resolved?

@RyanCavanaugh
Copy link
Member

Design limitation means "No apparent way of fixing this exists (short of wholescale redesign)". "Won't Fix" would mean "We could fix it but choose not to".

@buschtoens
Copy link

I see. Thanks for clarifying! 👌🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

6 participants