Home > Net >  Specify Order of Variadic Generic Arguements for Variable Parameters in TypeScript
Specify Order of Variadic Generic Arguements for Variable Parameters in TypeScript

Time:11-22

I'm trying to do something that feels absurd, but I've gotten so close I feel it must be possible, I just can't quite get it.
I'm trying to create a generic function type such that assigning the function type

const typedFunction: Generic<
    SomeObject,
    ['keys', 'in', 'that', 'object']
>;

will produce a function whose arguments are typed, exactly:

(
    arg0: typeof SomeObject['keys'],
    arg1: typeof SomeObject['in'],
    arg2: typeof SomeObject['that'],
    arg3: typeof SomeObject['object']
)

I've arrived at two near-but-not-quite solutions;

1.

declare type SomeGeneric<
   T,
   Params extends (T[keyof T])[]
> = (...args: Params) => ReturnType;

Which preserves the order, but forces me to specify Params in a very ugly way:

const someFunction: SomeGeneric<
    SomeObject,
    [SomeObject['keys'], SomeObject['in'], ...]
> = (arg0, arg1, arg2, arg3) => { ... };

This is obviously verbose to the point of being useless, as I could just specify them directly that way.

2.

declare type SomeGeneric<
    T,
    Params extends (keyof T)[]
> = (...args: T[Params[number]][]) => ReturnType;

But, since Params[number] refers to any member of Params, this turns every argument of SomeGeneric<SomeObject, ['keys', 'in', 'that', 'object']> into a union type of SomeObject['keys'] | SomeObject['in'] | ...

What I'd like to know is if there's some way to specify that I want SomeObject to be accessed via the keys provided in order, something like ...args: SomeObject[...Params[number]] if such syntax weren't nonsensical.

CodePudding user response:

You were pretty close with your 2nd attempt.

You need to use mapped type to map over the elements in Params. For each index I you can get the array element Params[I] which can be used to T.

Note that using a mapped type to map over tuple also produces a tuple since 3.1. This is essential here since a spread parameter type must be a tuple type.

declare type SomeGeneric<
   T,
   Params extends (keyof T)[]
> = (...args: { [I in keyof Params]: T[Params[I]] }) => void;


type Test = {
    a: string
    b: number
    c: Date
}

const someFunction: SomeGeneric<Test, ["a", "b", "c"]> = (
    arg0, // string
    arg1, // number
    arg2  // Date
) => {}

Playground

  • Related