TypeScript:使用元组实现管道功能

TypeScript: implementing pipe function using tuple

Playground Link

我正在实现一个管道函数,但与常规实现有一个区别:它不是将函数作为参数,而是必须接受作为单个参数传递的函数数组。

与大多数实现一样,我的管道实现使用类型变量捕获每个函数的输入和输出类型。

// simplified without overloads
type Fn<I, O> = (i: I) => O;

function pipeWithArr<A, B>(params: [Fn<any, A>, Fn<A, B>]) {
  console.log(params);
}

为了比较,像 https://gcanti.github.io/fp-ts/modules/function.ts.html#pipe 这样的常见实现看起来像这样:

function pipeWithArgs<A, B>(a: Fn<any, A>, b: Fn<A, B>) {
  console.log(a);
}

虽然这两种实现的工作方式相似,但前者在推断输入和输出类型变量方面的能力较差——特别是当这些类型由数组本身内的泛型函数推断时。

const source = <O>(data: O): Fn<any, O> => () => data;
const transform = <I, O>(fn: (i: I) => O) => (i: I) => fn(i);

// Success!
// A and B inferred as desired – pipeWithArgs<A = { a: number }, B = number>
pipeWithArgs(
  source({ a: 1 }),
  transform((result) => result.a)
);

// Error - Property 'a' does not exist on type 'unknown'
// A and B typed as unknown – pipeWithTuple<A = unknown, B = unknown>
pipeWithArr([source({ a: 1 }), transform((result) => result.a)]);

我们可以帮助编译器推断出A和B:

// By not using generic functions
pipeWithArr([() => ({ a: 1 }), transform((result) => result.a)]);

// Or explictly typing the generics
pipeWithArr([
  source<{ a: number }>({ a: 1 }),
  transform((result) => result.a),
]);

但是,pipeWithArr 的用户希望能够: a) 能够将通用函数传递给它 b) 不需要提供显式类型

是否有任何解决方法可以使 pipeWithArrpipeWithArgs 一样工作,或者这是编译器的限制?或者,对它应该如何工作有误解?

目前似乎不可能 (v4.6)。

Meta-issue: Use Full Unification for Generic Inference? #30134 跟踪总体问题。

This comment 可能会阐明 TS 在这种情况下用于推断的机制。