考虑如下
Option
ADT 的代码(与 您的代码的一个问题是它不正确,因此编译器实际上根本没有希望正确推断类型。正确的实现如下所示:
const mapOption: MapOption = f => fa => (
match(
() => none,
value => some(f(value)) // error!
// -----------> ~~~~~
// Argument of type 'unknown' is not assignable to parameter of type 'A'.
)
)(fa);
但是,正如您所看到的,推理问题仍然存在。编译器无法从
context推断
value
的类型,因此它会回退到 unknown
类型。
这种事情归根结底是 TypeScript 的设计限制。该语言不能总是推断“通用”类型参数并同时从上下文推断类型。当前关于一般问题的开放问题是 microsoft/TypeScript#47599。 虽然多年来这里已经有所改进,但它可能总会以某种形式存在。 TypeScript 不使用
fullunificiation 算法进行类型推断,如其他一些函数式编程语言中那样,以及 microsoft/TypeScript#30134 中的要求。 它可能永远不会使用这样的算法,因为(根据
microsoft/TypeScript#17520 上的评论),当前的推理算法“具有能够在不完整的代码中进行部分推理的明显优势,这是非常有益的在 IDE 中完成语句。”
如果您希望代码能够编译,我们必须解决这个问题。一种方法是做您不想做的事情:“内联”类型。也许是这样的:const mapOption: MapOption = f => fa => (
match(
() => none,
(value: Parameters<typeof f>[0]) => some(f(value))
)
)(fa);
这里我使用
Parameters
实用程序类型
来获取与
A
对应的匿名泛型类型参数的句柄。否则,您必须非常明确地执行此操作并实际声明类型参数:const mapOption3: MapOption = <A, B>(f: (x: A) => B) => (fa: Option<A>) => (
match(
() => none,
(value: A) => some(f(value))
)
)(fa);
另一种解决方法是利用一些无点样式
来消除fa
,从而通过缩短预期返回类型和所需输入类型之间的“距离”来使类型推断变得更容易:
const mapOption: MapOption = f => match(
() => none,
value => some(f(value))
);
这种方式对于特定的代码示例可能是理想的,但您可能通常需要诉诸于早期的方法之一。如果没有完全统一,您将不得不在某些可能会被推断出的地方指定类型。Playground 代码链接