ramda.js - Ramda 转换器上的 Typescript 类型错误及如何处理良好代码中的混乱类型错误
查看 Ramda 关于 transduce 的文档,有两个示例,每个示例都会导致 Typescript 编译器抛出不同的错误。
示例 1:
test('ex. 1', () => {
const numbers = [1, 2, 3, 4]
const transducer = compose(
map(add(1)),
take(2)
)
const result = transduce(transducer, flip(append), [], numbers)
expect(result).toEqual([2, 3])
})
Typescript 对 flip(append) 抛出以下异常:
Argument of type '(arg1: never[], arg0?: {} | undefined) => <T>(list: readonly T[]) => T[]' is not assignable to parameter of type '(acc: ({} | undefined)[], val: {} | undefined) => readonly ({} | undefined)[]'.
Types of parameters 'arg1' and 'acc' are incompatible.
Type '({} | undefined)[]' is not assignable to type 'never[]'.
Type '{} | undefined' is not assignable to type 'never'.
Type 'undefined' is not assignable to type 'never'.
如果我将 flip(append) 改为 flip(append) as any,代码就能按预期工作。
示例 2:
test('ex. 2', () => {
const isOdd = x => x % 2 === 1
const firstOddTransducer = compose(
filter(isOdd),
take(1)
)
const result = transduce(
firstOddTransducer,
flip(append) as any,
[],
range(0, 100)
)
expect(result).toEqual([1])
})
Typescript 对 firstOddTransducer 抛出以下异常:
Argument of type '(x0: readonly any[]) => Dictionary<any>' is not assignable to parameter of type '(arg: any[]) => readonly any[]'.
Type 'Dictionary<any>' is missing the following properties from type 'readonly any[]': length, concat, join, slice, and 16 more.
与上面相同,如果我将 firstOddTransducer 改为 firstOddTransducer as any,代码就会按预期工作。
首先,这些特定的错误到底意味着什么?
其次,处理这些与函数式 TypeScript 相关的问题的最佳方法是什么?在查看各种 TypeScript 学习资源时,用户常常被警告不要使用 any 或 // @ts-ignore,仿佛这是你永远不应该做的事情,但我的代码库越复杂,我的编程风格越函数式,我收到的这些看似难以理解的错误信息就越多,而这些代码实际上是完全可以接受的。我不介意花一点时间来改进类型,但我不想在类型调试上花太多时间,尤其是在我知道代码是正确的时候。
第三,当你不太确定问题出在类型或 TypeScript(如上所述),还是出在 JavaScript 代码时,有没有一些技巧可以提供帮助,例如确定实际问题所在的位置,以便你可以调查或忽略?
答案#1
截至撰写本文时,这是 TypeScript 的一个限制,你根本无法表达类型的某部分必须从后续使用中推断出来。
直到你调用 transduce 时,你才会真正绑定所有类型参数,因此在那之前类型并不完整,或者更确切地说,TypeScript 在你执行此操作之前不知道如何完成类型。TypeScript 会尝试从上下文中推断类型,所以也许如果你把所有内容放在一行,它可能能够做到这一点(并不是说它一定能做到)。
从根本上讲,变换器是一种对序列/流/可观察对象的抽象,当你实际在一些数据上运行你的变换器时,你会将这些信息提供给编译器。这时,它需要绑定数据的类型信息。
我在这里唯一能想到的方法是,你先将 unknown 作为数据的占位类型,直到你运行 transduce 或 into。这不会提供类型安全,但它会压制编译器警告,而这些警告无论如何都是误报。这并不完美,但应该可行。