TypeScript:如何将类型“ getter函数的n元组”转换为“返回值的n元组”?

时间:2019-03-30 09:36:18

标签: typescript types typescript-typings

让我们假设您有一个函数,该函数具有一个getter函数数组作为输入,并返回一个具有相同返回值的数组(长度相同)。

在下面的示例中,类型定义type ValueTupleOf<...> = ...的外观如何?

function getValues<A extends (() => any)[]>(getters: A): ValueTupleOf<A> {
  return getters.map(getter => getter())
}

编辑:当然,我可以执行以下操作,但我希望有更好的解决方案:

function getValues<A1>(getters: [() => A1]): [A1]
function getValues<A1, A2>(getters: [() => A1, () => A2]): [A1, A2]
function getValues<A1, A2, A3>(getters: [() => A1, () => A2, () => A3]): [A1, A2, A3]
// ... etc
function getValues(getters: any): any {
  return getters.map(getter => getter())
}

2 个答案:

答案 0 :(得分:2)

您在这里...(不言自明)此外,demo

type ReturnTypes<T> = {
  [K in keyof T]: T[K] extends (...args: any[]) => infer R ? R : never
}

type Getter<T = {}> = () => T;
function getValues<Getters extends Array<Getter>>(getters: Getters) {
  return <ReturnTypes<Getters>>getters.map(getter => getter())
}

let values = getValues(<[() => "a1", () => "a2"]>[() => "a1", () => "a2"])
/* type of values is ["a1", "a2"] (hover to check)
In latest TS version you can use <const> to make tuples,
instead of writing things twice like I did here
*/

type foo = ReturnTypes<[() => "a1", () => "a2"]>;
// foo is ["a1", "a2"]

getValues(<[(x: string) => "a1"]>[(x: string) => "a1"])
// Also type-safe ^ gives error 

答案 1 :(得分:1)

简短答案

坚持超载。一开始他们可能看起来像很多工作,但替代方案甚至更糟。

好答案

TypeScript没有可变参数类型,因此,为了保留元组的顺序,您必须使用技巧。但是,此技巧很丑陋且有局限性。

import { Reverse } from 'typepark';

type Thunk<T> = () => T;

type ReturnTypesOf<T extends Thunk<any>[], Length extends number = T['length'], Queue extends any[] = [], Index extends number = Queue['length']> = {
  done: Queue;
  next: ReturnTypesOf<T, Length, Prepend<ReturnType<T[Index]>, Queue>>;
}[Yield<Queue, Length>];

type Yield<Queue extends any[], Length extends number> =
  Queue['length'] extends Length
    ? 'done'
    : 'next';

type Prepend<T, U extends any[]> =
  ((head: T, ...tail: U) => void) extends ((...all: infer V) => void)
    ? V
    : [];

type T1 = ReturnTypesOf<[() => 1, () => 'foo']>; // ["foo", 1]
type T2 = Reverse<T1>; // [1, "foo"]

如您所见,它可以解决问题,但是:

  • 很难理解
  • 收益率相反
  • 在反转相反顺序时,可能会陷入类型实例化过深甚至无限的情况。情况。

@jcalz的答案

注释中提到的

jcalz可以使用映射的元组类型。如果您不介意一个接一个地传递参数(而不是传递数组),则可以这样做:

type AnyFunction = () => any;

/**
 * Mapped type, but for tuples.
 */
type ReturnTypesOf<T extends AnyFunction[]> = {
  [K in keyof T]: T[K] extends AnyFunction
    ? ReturnType<T[K]>
    : never
};

function getValues<T extends AnyFunction[]>(...functions: T) {
  return functions.map(callback => callback()) as ReturnTypesOf<T>;
}

const values = getValues(
  () => "foo",
  () => 42
)