1

Take the following code (admittedly a contrived example):

arbitraryFunction(
    // Dynamically generated input
    [
        returnValue("key1", "a"), 
        returnValue("key2", 1),
        returnValue("key3", ()=>{})
    ],

    // Callback, parameters are typed against input
    (value : {key1: string, key2: number, key3: Function} )=>{
        return value 
    }

)

I'm hoping to type the value parameter in the callback in reference the the given input.

So for example, this callback function would be rejected:

// key1's type is string, not number!
(value : {key1: number} )=>{}

I've tried using generics, but it doesn't seem to work. Does anybody have any insight on if this is possible?

1 Answer 1

1

Yes - this can be done. You need to use recursive array iteration within your generics to iterate through the array of ReturnValues to build up the parameters for the callback.

Let me know if there's something you want further explanation on as there's a lot in here!

function returnValue<K extends string, T>(key: K, type: T): [K, T] {
    return [key, type];
}

type ReturnValue = ReturnType<typeof returnValue>;

type GenerateCallbackParams<I extends readonly ReturnValue[], P = {}> =
    // Base case where we have one item left in I
    I extends  readonly [infer R]
        ? R extends [infer K, infer T]
            ? K extends string 
                // Merge params with K: T
                ? P & { [key in K]: T }
                : P
            : never
        // When multiple array elements in I
        : I extends readonly [infer R, ...(infer Tail)]
            ? R extends [infer K, infer T]
                ? K extends string
                    ? Tail extends ReturnValue[]
                        ? GenerateCallbackParams<Tail, P & { [key in K]: T }>
                        : never
                    : never
                : never
            : never;

function arbitraryFunction<I extends readonly ReturnValue[]>(inputParams: I, callback: (params: GenerateCallbackParams<I>) => GenerateCallbackParams<I>) {

}

// Has to be declared as const here otherwise TS widens the type inside the array
const params = [
    returnValue("key1", "a"), 
    returnValue("key2", 1),
    returnValue("key3", ()=>{})
] as const;

arbitraryFunction(
    // Dynamically generated input
    params,

    // Callback, parameters are typed against input
    ({ key1, key2, key3}) => {
        typeof key1; // string
        typeof key2; // number
        typeof key3; // () => void
        return { key1, key2, key3 };
    }
)

Link to playground

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

Comments

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.