打字稿减少了一个函数数组 [英] Typescript reduce an array of function

查看:24
本文介绍了打字稿减少了一个函数数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有一个函数数组,其中每个函数都接受前一个函数的返回值,我在该函数上调用 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆