TypeScript:使用元组实现管道功能
TypeScript: implementing pipe function using tuple
我正在实现一个管道函数,但与常规实现有一个区别:它不是将函数作为参数,而是必须接受作为单个参数传递的函数数组。
与大多数实现一样,我的管道实现使用类型变量捕获每个函数的输入和输出类型。
// 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) 不需要提供显式类型
是否有任何解决方法可以使 pipeWithArr
像 pipeWithArgs
一样工作,或者这是编译器的限制?或者,对它应该如何工作有误解?
目前似乎不可能 (v4.6)。
Meta-issue: Use Full Unification for Generic Inference? #30134 跟踪总体问题。
This comment 可能会阐明 TS 在这种情况下用于推断的机制。
我正在实现一个管道函数,但与常规实现有一个区别:它不是将函数作为参数,而是必须接受作为单个参数传递的函数数组。
与大多数实现一样,我的管道实现使用类型变量捕获每个函数的输入和输出类型。
// 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) 不需要提供显式类型
是否有任何解决方法可以使 pipeWithArr
像 pipeWithArgs
一样工作,或者这是编译器的限制?或者,对它应该如何工作有误解?
目前似乎不可能 (v4.6)。
Meta-issue: Use Full Unification for Generic Inference? #30134 跟踪总体问题。
This comment 可能会阐明 TS 在这种情况下用于推断的机制。