typescript 键入数组中项之间的关系

r6hnlfcb  于 2023-06-24  发布在  TypeScript
关注(0)|答案(2)|浏览(95)

我想创建一个函数,它接受一个函数数组,数组中的每一项都按顺序执行,结果与其他一些数据沿着传递给下一项。下面是一个我想做的事情的例子:

callExample([
  (stuff) => 123,
  (stuff, previousResult) => previousResult ** 2,
  (stuff, previousResult) => String(previousResult)
])

我希望能够输入这个,这样我就知道previousResult的类型是什么,并将callExample的返回类型输入为数组中最后一个函数的返回类型。
我意识到

type Example<A, B, C> = ([
  (stuff: unknown) => A,
  (stuff: unknown, previous: A) => B,
  (stuff: unknown, previous: B) => C,
]) => C

适用于已知数量的元素,但是否有一种方法来键入关系本身并支持动态数量的项目?

olmpazwi

olmpazwi1#

可以使用元组类型沿着rest运算符...

type MyArguments<A, B> = [
  (stuff: unknown) => A, // First element
  ...((stuff: unknown, previous: A) => B)[] // All other elements
]

那么MyArguments将是可变长度元组类型,其中第一个元素类型是(stuff: unknown) => A,其余元素的类型是(stuff: unknown, previous: A) => B)
那么你的Example类型将是:

type Example<C> = (args: MyArguments) => C

TypeScript手册中的更多信息

3okqufwl

3okqufwl2#

算法:

  • T是函数数组
  • 获取T的第一个元素,检索stuff的类型,并返回第一个函数的类型
  • 调用T的其余部分的递归类型
  • FuncT其余部分中的第一个元素
  • Result数组添加一个函数,该函数接受具有定义类型和前一个值的stuff,前一个值是前一个函数的返回类型
  • 更新先前的值以返回Func的类型
  • 如果T为空,则返回Result
  • 将结果与第一个元素展开以获得完整的数组。

实施:

type UnknownFunc = (...args: any[]) => any;

type _ChainFunctions<
  T,
  FirstArgument,
  PrevTo,
  Result extends readonly unknown[] = readonly [],
> = T extends readonly [infer Func extends UnknownFunc, ...infer Rest]
  ? _ChainFunctions<
      Rest,
      FirstArgument,
      ReturnType<Func>,
      readonly [
        ...Result,
        (stuff: FirstArgument, previousResult: PrevTo) => ReturnType<Func>,
      ]
    >
  : Result;

type ChainFunctions<T extends readonly UnknownFunc[]> = T extends readonly [
  infer First extends UnknownFunc,
  ...infer Rest,
]
  ? readonly [
      First,
      ..._ChainFunctions<Rest, Parameters<First>[0], ReturnType<First>>,
    ]
  : T;

我们正在使用inject关键字检索函数数组的元素。
要获得stuff的类型,我们使用内置的参数实用程序类型
用途:

const func = <T extends readonly UnknownFunc[]>(arg: ChainFunctions<T>) => {};

我们需要使用constAssert来防止编译器扩大输入数组的类型。ConstAssert将把数组转换为readonly,这就是为什么我们到处都有readonly的原因:

func([
  (stuff: number) => 123,
  (stuff: number, previousResult: number) => previousResult ** 2,
  (stuff: number, previousResult: number) => String(previousResult),
] as const);

func([
  (stuff: number) => 123,
  (stuff: number, previousResult: string) => previousResult ** 2, // expected error
  (stuff: number, previousResult: number) => String(previousResult),
] as const);

func([
  (stuff: number) => 123,
  (stuff: string, previousResult: number) => previousResult ** 2, // expected error
  (stuff: number, previousResult: number) => String(previousResult),
] as const);

链接到Playground

相关问题