根据变量输入键入回调函数

Typing callback functions based on variable inputs

采用以下代码(诚然是一个人为的例子):

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 
    }

)

我希望在回调中键入 value 参数以引用给定的输入。

例如,这个回调函数将被拒绝:

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

我尝试过使用泛型,但它似乎不起作用。有人知道这是否可行吗?

是的 - 这是可以做到的。您需要在泛型中使用递归数组迭代来遍历 ReturnValues 数组以构建回调参数。

如果您需要进一步解释,请告诉我,因为这里有很多内容!

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