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.
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
) => {}