Typescript: Get the type of the last parameter of a function type

后端 未结 4 804
花落未央
花落未央 2021-01-05 16:32

Assume I have a function type, e.g.

type somefntype = (a: number, b: string, c: boolean) => void

I need the type of that function types

相关标签:
4条回答
  • 2021-01-05 17:06

    As of TypeScript 4.0 with variadic tuple support, it's also possible to use the following to get the last type in a tuple type:

    type LastType<T extends [unknown, ...Array<unknown>]> = T extends [...infer A, infer L] ? L : never;
    
    0 讨论(0)
  • 2021-01-05 17:10

    For function with 3 args solution wil be

    type somefntype = (a: number, b: string, ffffd: boolean) => void
    type LastParameter<T> = T extends (a: infer A1, b: infer A2, c: infer A3) => any ? A3 : never;
    
    type lastArg = LastParameter<somefntype>;  // === boolean
    

    Name of function agruments does not metter.

    For function with arbitrary number of arguments task become a bit complicated. We may use ...rest but it will be array.

    type LastParameter<T> = T extends (...rest: infer A3) => any ?
        A3 : never;
    type lastArg = LastParameter<somefntype>; // lastArg === [number, string, boolean]
    

    Get last element of resulting tuple is a bit challenging. One possible solution has been inspired from this article.

    type Prev<T extends number> = [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62][T];
    type GetLength<original extends any[]> = original extends { length: infer L } ? L : never;
    type GetLast<original extends any[]> = original[Prev<GetLength<original>>]
    
    type somefntype = (a: number, b: string, ffffd: boolean) => void
    type LastParameter1<T> = T extends (...rest: infer A3) => any ?
        A3 extends any[] ? GetLast<A3> : never
        : never;
    type lastArg = LastParameter<somefntype>; // boolean for 3 arg function
    

    Second solution has limitaion on number of arguments of the function.

    0 讨论(0)
  • 2021-01-05 17:16

    Update: TypeScript 4.0 introduced variadic tuple types, which means that Last can now be implemented simply as

    type Last<T extends any[]> = T extends [...infer I, infer L] ? L : never; 
    

    And the rest works as normal:

    type LastParameter<F extends (...args: any)=>any> = Last<Parameters<F>>;
    type somefntype = (a: number, b: string, c: boolean) => void
    type lastparamtype = LastParameter<somefntype> // boolean
    

    Playground link to code


    TypeScript 3.0 introduced tuple types in rest and spread expressions, specifically to allow programmatic conversion between the types of function parameter lists and tuples. There's a type alias called Parameters<F> defined in the standard library that returns the parameter list of a function as a tuple.

    It's annoying but possible to get the last element of a tuple type with a fixed but arbitrary length:

    // Tail<T> returns a tuple with the first element removed
    // so Tail<[1, 2, 3]> is [2, 3]
    // (works by using rest tuples)
    type Tail<T extends any[]> = 
      ((...t: T)=>void) extends ((h: any, ...r: infer R)=>void) ? R : never;
    
    // Last<T> returns the last element of the tuple
    // (works by finding the one property key in T which is not in Tail<T>)
    type Last<T extends any[]> = T[Exclude<keyof T, keyof Tail<T>>];
    

    Putting those together you get

    type LastParameter<F extends (...args: any)=>any> = Last<Parameters<F>>;
    

    And we can test it:

    type somefntype = (a: number, b: string, c: boolean) => void
    type lastparamtype = LastParameter<somefntype> // boolean
    

    Looks good to me. Link to code

    0 讨论(0)
  • 2021-01-05 17:18

    Here is yet a another solution which is a slight modification of jcalz answer to make it more generic using some recursive conditional types:

    type Head<T extends any[]> = T extends [any, ...any[]] ? T[0] : never;
    type Tail<T extends any[]> =
        ((...t: T) => any) extends ((_: any, ...tail: infer U) => any)
            ? U
            : [];
    type HasTail<T extends any[]> = T extends ([] | [any]) ? false : true;
    
    type Last<T extends any[]> = {
        0: Last<Tail<T>>
        1: Head<T>
    }[
        HasTail<T> extends true ? 0 : 1
    ];
    
    type LastType = Last<Parameters<somefntype>>; // boolean
    

    It might be interesting to consider replacing any with unknown as well.

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