Skip to content

"...args: infer" on an overloaded function chooses only one function head #26136

Description

@mike-north

TypeScript Version: 3.1.0-dev.20180801

Search Terms: overload, infer, function heads, tuple, rest parameter

Code

interface Foo {
  (a: string, b: number, c: boolean): void;
  (a: number, b: string, c: boolean): void;
}

type Args<F> = F extends (...args: infer T) => any ? T : never;

let x: Args<Foo> = [42, 'hello world', true]; // ✅
x = ['hello world', 42, true]; // 💥

Expected behavior:
Ideally, I would hope for the typeof x to be

let x: [string, number, boolean] | [number, string, boolean]

although not entirely correct, I would even take a type wide enough to accomodate both function heads.

let x: [string|number, number|string, boolean]

Actual behavior:
The type of x ends up being

let x: [number, string, boolean]

Playground Link: https://www.typescriptlang.org/play/#src=interface%20Foo%20%7B%0D%0A%20%20(a%3A%20string%2C%20b%3A%20number%2C%20c%3A%20boolean)%3A%20void%3B%0D%0A%20%20(a%3A%20number%2C%20b%3A%20string%2C%20c%3A%20boolean)%3A%20void%3B%0D%0A%7D%0D%0A%0D%0Atype%20Args%3CF%3E%20%3D%20F%20extends%20(...args%3A%20infer%20T)%20%3D%3E%20any%20%3F%20T%20%3A%20never%3B%0D%0A%0D%0Alet%20x%3A%20Args%3CFoo%3E%20%3D%20%5B42%2C%20'hello%20world'%2C%20true%5D%3B%20%2F%2F%20%E2%9C%85%0D%0Ax%20%3D%20%5B'hello%20world'%2C%2042%2C%20true%5D%3B%20%2F%2F%20%F0%9F%92%A5

Related Issues: #24897, #5453 (maybe)

Activity

  1. mike-north commented on Aug 2, 2018

    @mike-north
    Author

    Closing as duplicate of Brian Terlson (@bterlson)'s #26132

  2. bterlson commented on Aug 2, 2018

    @bterlson
    Member

    Mike North (@mike-north) is this a duplicate, though? #26132 doesn't have overloads, and arguments list inference of fn gives something similar to what you request here:

    // sample from #26132
    
    type Rec = {
      foo: (a: string, b: number) => void,
      bar: (b: boolean, c: RegExp) => void,
    }
    
    type Parameters<T> = T extends (...args: infer U) => any ? U : never;
    
    declare var fn: <K extends keyof Rec>(e: K, ...args: Parameters<Rec[K]>) => void;
    
    // test parameter extraction
    type test = Parameters<typeof fn>;
    // type test = ["foo" | "bar", ...(string | number | boolean | RegExp)[]]
  3. mike-north commented on Aug 2, 2018

    @mike-north
    Author

    Good catch, I must have read your issue too quickly. Maybe this should be un-tagged as duplicate

  4. added
    SuggestionAn idea for TypeScript
    Too ComplexAn issue which adding support for may be too complex for the value it adds
    and removed
    DuplicateAn existing issue was already created
    on Aug 2, 2018
  5. RyanCavanaugh commented on Aug 7, 2018

    @RyanCavanaugh
    Member

    Discussed for a while yesterday. This looks reasonably straightforward if you're just capturing the entire parameter list into a single spread, but quickly turns into a bunch of really complicated questions once you do anything else

    • What would the behavior of type Args<F> = F extends (n: number, ...args: infer T) => any ? T : never; be? Would it be sound to just capture the matching overloads, or not?
    • What happens when you put T into a different argument list? Potentially a combinatorially explosive expansion in to overloads?
    • More problems I can't recall at the moment

    Overall too complex unless there are some important use cases that appear.

  6. spiritwindy commented on Aug 15, 2018

    @spiritwindy

    Ryan Cavanaugh (@RyanCavanaugh)
    some example like
    Function.prototype.call
    Function.prototype.apply
    Function.prototype.bind

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

    SuggestionAn idea for TypeScriptToo ComplexAn issue which adding support for may be too complex for the value it adds

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions