【问题标题】:Lodash Flow and TypeScriptLodash Flow 和 TypeScript
【发布时间】:2019-11-04 02:14:03
【问题描述】:

我一直在关注一篇关于 Lodash 的文章,Why using _.chain is a mistake,它强调您可以使用 Flow 消除对链的需求。

给出的例子如下使用链

import _ from "lodash";

_.chain([1, 2, 3])
    .map(x => [x, x*2])
    .flatten()
    .sort()
    .value();

可以使用flow转换为以下

import map from "lodash/fp/map";
import flatten from "lodash/fp/flatten";
import sortBy from "lodash/fp/sortBy";
import flow from "lodash/fp/flow";

flow(
    map(x => [x, x*2]),
    flatten,
    sortBy(x => x) 
)([1,2,3]);

但是,当我使用 TypeScript 实现它时,我收到以下错误

对象的类型为“未知”.ts(2571)

有没有办法解决这个问题,以便 TypeScript 知道它正在处理哪些类型?

【问题讨论】:

  • 由于答案中已经列出的原因,ts 编译器因长函数组合而受到打击和错过。如果你喜欢 _.chain 的类型安全和流畅的风格,你可能会在 monetjs 中实现 List 数据类型。 github.com/monet/monet.js/blob/master/docs/LIST.md

标签: javascript typescript functional-programming lodash


【解决方案1】:

我制作了一个帮助程序,让您可以将数组作为第一个参数传入,运算符作为下一个参数传入,这样您就可以进行完整的输入:

import { orderBy, map, filter, flow } from 'lodash/fp'

export function chainFlow<T, R1, R2, R3, R4, R5>(
  values: T,
  f1: (t: T) => R1,
  f2: (t: R1) => R2,
  f3: (t: R2) => R3,
  f4: (t: R3) => R4,
  f5: (t: R4) => R5
): R5
export function chainFlow<T, R1, R2, R3, R4>(
  values: T,
  f1: (t: T) => R1,
  f2: (t: R1) => R2,
  f3: (t: R2) => R3,
  f4: (t: R3) => R4
): R4
export function chainFlow<T, R1, R2, R3>(
  values: T,
  f1: (t: T) => R1,
  f2: (t: R1) => R2,
  f3: (t: R2) => R3
): R3
export function chainFlow<T, R1, R2>(
  values: T,
  f1: (t: T) => R1,
  f2: (t: R1) => R2
): R2
export function chainFlow<T, R1>(values: T, f1: (t: T) => R1): R1
export function chainFlow<T>(
  values: T,
  ...funcs: ((...args: any[]) => any)[]
): any {
  return flow(funcs)(values)
}

// Example usage
const x = [1,2,3,4]

const y = chainFlow(x, filter(x => x > 3), map(x => x + "abc"))

全智能:

【讨论】:

  • 这看起来很有希望,但不适用于所有功能。我尝试使用 orderBygroupBy 失败,出现编译器错误。
【解决方案2】:

当 JavaScript/TypeScript 被解释时(注意:TypeScript 代码被转译为 JavaScript,然后被解释),当你使用链时,typescript 知道它处理的类型(即数字数组),因为它首先出现.但是对于流,流函数将要作用的类型是最后指定的,因此它不知道它将使用什么类型,因此需要泛型或其他指示将要使用的类型的方式。此外,生成此错误的 linter 正在解释代码,因此在使用“flow”时,它不知道输入最后出现时正在处理的类型。

当您将 lodash 与 TypeScript 一起使用时,您使用的函数支持泛型来指定它们正在处理的类型。

因此,您可以如下说明从 lodash 函数输入/输出的内容:

flow(
    map<number, [number, number]>(x => [x, x * 2]),
    flatten,
    sortBy(x => x)
)([1, 2, 3]);

或者像达雷卡建议的那样:

flow(
    map((x: number) => [x, x * 2]),
    flatten,
    sortBy(x => x)
)([1, 2, 3])

但这同样有效,因为我们向编译器指示将要执行的类型是什么。

可以在here 找到这些代码 sn-ps 工作的示例。

注意函数输入/输出是如何被明确定义的,例如“map”表示它要迭代的类型是一个数字,运算的结果是一个元组。

【讨论】:

  • 谢谢,但是这样做比做可以计算类型的链有什么优势?当然,通过必须通过泛型指定类型,它可以避免从链/组合切换?
  • 是的,我已经阅读了那篇文章(它在原始帖子中链接),但是可以像sortBy(flatten(map([1, 2, 3], x =&gt; [x, x * 2])), x =&gt; x) 那样在流方法上使用组合,并且像这样使用它,类型仍然可以计算出来.
  • 对不起@Dan。我刚刚注意到您在问题中包含了这篇文章。我认为问题在于,使用 vanilla javascript 您不必指定类型,如果您正确组合了函数,它将“正常工作”。然而,当切换到使用 Typescript 时,它可以推断出某些类型,但是由于 lodash/fp 函数上的重载量,它需要通过更明确地使用泛型来“帮助”。这个问题我也遇到过和你一样的悲痛!
  • @Dan 并将其视为 javascript/typescript 被解释为未编译,当您使用链时,typescript 知道其处理的类型(即数字数组)。但是对于流,流函数将要作用的类型是最后指定的,因此它不知道它将使用什么类型,因此需要泛型。我将把它放在答案中。
  • 我猜泛型是有道理的。这有点令人失望。至于编译的东西,其实就是typescript编译的。
【解决方案3】:

_.flow() 方法生成一个pointfree 函数。这可以防止打字稿推断输入的类型。

要解决这个问题,请将函数分配给一个变量,并添加函数的类型。声明函数参数和输出时,typescript 可以推断流内部函数的内部类型。

将函数赋值给变量后,可以调用它(sandobx):

const fn: (numbers: number[]) => number[] = flow(
    map(x => [x, x*2]),
    flatten,
    sortBy(x => x) 
);

const result = fn([1,2,3]);

如果你想立即调用函数,或者使用类型断言(sandbox):

type Fn = (numbers: number[]) => number[];

const result = (flow(
  map(x => [x, x * 2]),
  flatten,
  sortBy(x => x)
) as Fn)([1, 2, 3]);

【讨论】:

    【解决方案4】:

    我喜欢 Ben Smith 的回答,因为它对我很有教育意义。但我会建议这个

    map((x: number) => [x, x*2])
    

    甚至

    map((x: any) => [x, x*2])
    

    对于更一般的情况。如果您不想太严格,它似乎有效并且会得到尽可能多的推断。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-25
      • 2021-05-21
      • 2019-06-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-10-11
      • 2016-08-20
      相关资源
      最近更新 更多