1

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.

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

3 Comments

Aha, I was trying to use X in keyof Y but I had no idea you could specify variable arguments as an object type rather than an array type. Thank you!
@JMA - it is a tuple type. See the 3rd paragraph (that I just edited in)
that makes more sense; the fixed/const nature of tuples is exactly why this works, and I understand that now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.