镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Function argument inference only handles one overload #26591

Description

@JakeTunaley

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

Activity

  1. RyanCavanaugh commented on Aug 22, 2018

    @RyanCavanaugh
    Member

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

  2. bdpartridge commented on Feb 4, 2019

    @bdpartridge

    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.

  3. ggoodman commented on Mar 29, 2019

    @ggoodman

    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.

  4. gogoyqj commented on Apr 2, 2019

    @gogoyqj

    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

  5. buschtoens commented on Jun 2, 2021

    @buschtoens

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

  6. RyanCavanaugh commented on Jun 2, 2021

    @RyanCavanaugh
    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".

  7. buschtoens commented on Jun 2, 2021

    @buschtoens

    I see. Thanks for clarifying! 👌🏼

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions