打字稿减少了一个函数数组 [英] Typescript reduce an array of function
问题描述
假设我有一个函数数组,其中每个函数都接受前一个函数的返回值,我在该函数上调用 Array#reduce
并使用数组中的第一个函数接受的初始值.这是完全合理的,我希望返回类型是最后一个函数的返回类型.
Say I have an array of function where each function accepts the return value of the previous function and I call Array#reduce
on that function with an initial value which the first function in the array accepts. This is perfectly sound and I would expect the return type to be the return type of the last function.
不过打字稿不会允许我这样做(看到操场).
However TypeScript will not allow me to do this (see playground).
更实用的是,我正在尝试编写一个通用的 pipe
函数,它将组合作为 ...rest
给出的函数并将第一个参数管道"到组合函数:
More pragmatically, I’m trying to write a generic pipe
function which will compose the functions given as the ...rest
and "pipe" the first argument into the composed function:
function pipe(source, ...fns) {
return fns.reduce((value, fn) => fn(value), source);
}
即使使用 可变元组类型.
即使我尝试递归地写出函数,我也不确定如何输入:
Even if I try to write out the function recursively I’m not really sure how to type it:
function pipe<
S,
R,
Fns extends readonly unknown[],
>(source: S, ...fns: [(source: S) => R, ...Fns]): R {
if (fns.length === 0) {
return source;
}
const [fn, ...rest] = fns;
return pipe(fn(source), rest);
}
参见操场.
推荐答案
它对你有用吗?
type Foo = typeof foo
type Bar = typeof bar
type Baz = typeof baz
type Fn = (a: any) => any
type Head<T extends any[]> =
T extends [infer H, ...infer _]
? H
: never;
type Last<T extends any[]> =
T extends [infer _]
? never : T extends [...infer _, infer Tl]
? Tl
: never;
// credits goes to https://stackoverflow.com/questions/55541275/typescript-check-for-the-any-type
type IfAny<T, Y, N> = 0 extends (1 & T) ? Y : N;
type IsAny<T> = IfAny<T, true, never>;
type HandleAny<T extends Fn, U> =
IsAny<Head<Parameters<T>>> extends true ?
(a: U) => ReturnType<T>
: T
type Allowed<
T extends Fn[],
Cache extends Fn[] = []
> =
T extends []
? Cache
: T extends [infer Lst]
? Lst extends Fn
? Allowed<[], [...Cache, Lst]> : never
: T extends [infer Fst, ...infer Lst]
? Fst extends Fn
? Lst extends Fn[]
? Head<Lst> extends Fn
? Head<Parameters<Fst>> extends ReturnType<Head<Lst>>
? Allowed<Lst, [...Cache, HandleAny<Fst, ReturnType<Head<Lst>>>]>
: never
: never
: never
: never
: never;
type LastParameterOf<T extends Fn[]> =
Last<T> extends Fn
? Head<Parameters<Last<T>>>
: never
type Return<T extends Fn[]> =
Head<T> extends Fn
? ReturnType<Head<T>>
: never
function compose<T extends Fn, Fns extends T[], Allow extends {
0: [never],
1: [LastParameterOf<Fns>]
}[Allowed<Fns> extends never ? 0 : 1]>
(...args: [...Fns] & Allowed<Fns>): (...data: Allow) => Return<Fns>
function compose<
T extends Fn,
Fns extends T[], Allow extends unknown[]
>(...args: [...Fns]) {
return (...data: Allow) =>
args.reduceRight((acc, elem) => elem(acc), data)
}
const foo = (arg: 1 | 2) => [1, 2, 3]
const bar = (arg: string) => arg.length > 10 ? 1 : 2
const baz = (arg: number[]) => 'hello'
/**
* Ok, but you need explicitly add allowed type
*/
const check = compose((a: string) => a, baz)([1, 2, 3]) // [number]
/**
* Errors
*/
// error because no type
const check_ = compose((a) => a, baz)([1, 2, 3])
// error because `a` expected to be string instead of number
const check__ = compose((a: number) => a, baz)([1, 2, 3])
这里,在我的博客中,你可以找到解释.如果您仍然对这个问题感兴趣,请告诉我,我会尝试提供更多示例或示例.
Here, in my blog, you can find an explanation. Let me know if you are still interested in this question, I will try to provide more examplations or examples.
这篇关于打字稿减少了一个函数数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!