Parameters generic of overloaded function doesn't contain all options

前端 未结 2 1659
误落风尘
误落风尘 2020-12-02 01:14

Given an overloaded function example.

function example(a: string): number
function example(a: string, b?: string): number { 
  return 1
}

type         


        
相关标签:
2条回答
  • 2020-12-02 02:06

    It is not possible. To quote the docs:

    When inferring from a type with multiple call signatures (such as the type of an overloaded function), inferences are made from the last signature (which, presumably, is the most permissive catch-all case). It is not possible to perform overload resolution based on a list of argument types.

    In your case, you have only one overload, as jonrsharpe points out correctly. Say, we add one for illustration:

    function example(a: string): number
    function example(a: string, b: string): number
    function example(a: string, b?: string): number { 
      return 1
    }
    

    Then the conditional type Parameters will always infer/pick the last overload (sample here):

    type Result = Parameters<typeof example> // Result = [string, string]
    
    0 讨论(0)
  • 2020-12-02 02:19

    In the answers to the question this duplicates the limitation mentioned in @ford04's answer here, that infer only looks at the last overloaded signature, is acknowledged.

    But this answer shows it's not completely impossible; you can tease out some information about overloads, at least for functions with up to some arbitrary fixed number of them. But it's hairy and ugly and there might be bugs in it, see microsoft/TypeScript#28867. Here's one way of doing it:

    type Overloads<T> =
      T extends {
        (...args: infer A1): infer R1; (...args: infer A2): infer R2;
        (...args: infer A3): infer R3; (...args: infer A4): infer R4
      } ? [
        (...args: A1) => R1, (...args: A2) => R2,
        (...args: A3) => R3, (...args: A4) => R4
      ] : 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
      ] : any
    
    type OverloadedParameters<T> =
      Overloads<T> extends infer O ?
      { [K in keyof O]: Parameters<Extract<O[K], (...args: any) => any>> } : never
    
    type OverloadedReturnType<T> =
      Overloads<T> extends infer O ?
      { [K in keyof O]: ReturnType<Extract<O[K], (...args: any) => any>> } : never
    

    The Overloads<T> type alias takes a function type T and returns a tuple of its call signatures (for up to four overloads). And OverloadedParameters<T> and OverloadedReturnType<T> map Parameters<T> and ReturnType<T> over that tuple, respectively.

    Let's see it in action (after correcting your example so that it actually has multiple overloads, as done in the other answer):

    function example(a: string): number
    function example(a: string, b: string): number
    function example(a: string, b?: string): number {
      return 1
    }
    
    type ExampleOverloads = Overloads<typeof example>
    // type ExampleOverloads = [(a: string) => number, (a: string, b: string) => number]
    
    type ExampleParameters = OverloadedParameters<typeof example>
    // type ExampleParameters = [[string], [string, string]]
    

    Looks reasonable to me. Okay, hope that helps; good luck!

    Link to code

    0 讨论(0)
提交回复
热议问题