如何将 Array#map 结果分配给可变元组`[P in keyof T]: ReturnType<T[P]>`?

How to assign Array#map results to variadic tuple `[P in keyof T]: ReturnType<T[P]>`?

我正在尝试创建一个强类型函数,该函数映射到 return 任意值的同类函数数组。

我对这两种方法都进行了试验,虽然它们都成功地 return 了正确的类型,但我在 return 值中遇到了错误:

const runAll = <T extends (() => any)[]>(
    array: [...T],
): {
    [P in keyof T]: ReturnType<T[P]>
} => {
    const result = [];
    for (let i = 0; i < array.length; i += 1) {
        result.push(array[i]());
    }
    return result;
}
// ^ Error: Type 'any[]' is not assignable to type '{ [P in keyof T]: ReturnType<T[P]>; }'.ts(2322)

const results = runAll([
    () => 'hello',
    () => 123,
    () => true,
]);

// const results: [string, number, boolean]

const runAll = <T extends (() => unknown)[]>(
    actions: [...T],
): {
    [P in keyof T]: ReturnType<T[P]>;
} => actions.map(action => action());

// ^ Error: Type 'unknown[]' is not assignable to type '{ [P in keyof T]: ReturnType<T[P]>; }'.ts(2322)


const results = runAll([
    () => 'hello',
    () => 123,
    () => true,
]);
// const results: [string, number, boolean]

匹配return类型的正确方法是什么?

执行此操作的最简单(也许唯一)方法是从 unknown[] return 由 .map 显式转换为 return 的复杂类型 runAll。我知道这感觉不对,但你知道转换总是安全的,所以在函数内部进行转换并不可怕。我离开了你的第二个例子,还修复了我从 return 类型得到的次要错误。 (我可能有较新的 TS 版本。) 结果:

type AllResults<T> = {
    [P in keyof T]: T[P] extends (() => unknown) ? ReturnType<T[P]> : never;
};

const runAll = <T extends Array<() => unknown>>(
    actions: [...T],
): AllResults<T> => actions.map(action => action()) as AllResults<T>;

const results = runAll([
    () => 'hello',
    () => 123,
    () => true,
]);
// const results: [string, number, boolean]

C考虑下一个例子:


type Fn = () => any

type MapPredicate<F> = F extends Fn ? ReturnType<F> : never

/**
 * Iterate through [array] argument and get return type
 */
type Mapped<
  Arr extends Array<unknown>,
  Result extends Array<unknown> = []
  > = Arr extends []
  ? []
  : Arr extends [infer H]
  ? [...Result, MapPredicate<H>]
  : Arr extends [infer Head, ...infer Tail]
  ? Mapped<[...Tail], [...Result, MapPredicate<Head>]>
  : Readonly<Result>;

/**
 * Overload
 * see docs https://www.typescriptlang.org/docs/handbook/functions.html#overloads
 */
function runAll<Cb extends Fn, Cbs extends Cb[]>(
  array: [...Cbs],
): Mapped<Cbs>

function runAll<Cb extends Fn, Cbs extends Cb[]>(
  array: [...Cbs],
) {
  return array.reduce<ReadonlyArray<ReturnType<Cbs[number]>>>(
    (acc, elem) => [...acc, elem()],
    []
  )
}

const results = runAll([
  () => 'hello',
  () => 123,
  () => true,
]); // [string, number, boolean]

Playground

显式 return type 通常不适用于遍历数组的函数。这里最好使用重载

我添加了 Mapped 助手。 IT 只是递归地遍历 array 参数类型和 returns return 回调类型。

类似于array.map(elem=>elem())

Here,在我的博客中,你可以找到更多有趣的例子