typescript - 在 typescript 中使用带柯里化(Currying)的文字时如何防止缩小泛型类型?

标签 typescript type-inference

我在为 Sanctuary(用于函数式编程的 js 库)设计类型时遇到问题。我想创建 Ord 类型,代表具有自然顺序的任何值。 Ord 是:

  1. 一些内置的原始类型:numberstringDateboolean
  2. 任何实现所需接口(interface)(compareTo 方法)的用户类型
  3. Ord 数组

为了表达它,我使用了(这对我来说很自然)联合类型(在使重复类型成为可能方面有一些变通方法),例如:

type Ord = boolean | Date | number | string | Comparable<any> | OrderedArray

interface OrderedArray extends Array<Ord> {}

(我提到的任何代码都可以在这个 typescript playground 中找到)。

然后,为例如创建类型定义compare 函数,我使用了带有类型约束的泛型类型,即类似于(注意它是柯里化(Currying)的):

function compare<T extends Ord>(a: T): (b: T) => number

不幸的是,在使用这种带有文字参数的方法时,例如(compare(2)(3),这是完全有效的代码), typescript 错误,'3' is not assignable to '2'。似乎在提供第一个参数的情况下,typescript 将类型参数缩小到 2(仅包含 2 值的文字类型),而它应该只缩小到 number 。有什么办法可以阻止他这样做吗?或者任何其他工作方法?

最佳答案

如果编译器doesn't do it,您也可以使用类型系统自己进行文字扩展。 (例如此处 T 的上下文类型包括 stringnumberboolean ):

type Widen<T> = 
  T extends string ? string : 
  T extends number ? number : 
  T extends boolean ? boolean : 
  T;

const compare = <T extends Ord>(x: T) => (y: Widen<T>) => 0;

所以 x将保持狭窄,但 y将被加宽。这将允许以下调用:

compare(1)(3);  // <1>(x: 1) => (y: number) => number

希望对您有所帮助;祝你好运!

Link to code


更新:如果您真的希望能够有选择地保留 T狭窄,你可以做以下复杂的事情:

type OptionallyWiden<X, T> = 
   [X] extends [never] ? 
     T extends string ? string : 
     T extends number ? number : 
     T extends boolean ? boolean : 
     T
  : T;

const compare = <X extends Ord = never, T extends Ord = X>(x: T) => (
  y: OptionallyWiden<X, T>
) => 0;

这给了你这种行为:

compare(1)(3); // okay
// const compare: <never, 1>(x: 1) => (y: number) => number

compare<1 | 2>(1)(3); // error now
//  ------------> ~
// 3 is not assignable to 1 | 2
// const compare: <1 | 2, 1 | 2>(x: 1 | 2) => (y: 1 | 2) => number

它通过使用两个类型参数来工作...第一个,X , 将默认为 never除非手动指定。如果Xnever , 的推断值 T将被加宽。如果X不是 never , 指定的值将被使用而不是加宽。

这里的灵 active 值得奇怪笨重的杂耍吗?我想这取决于您自己的理解,但我可能会保留原始的 <T extends Ord>(x: T) => (y: T) => 0如果我想灵活地同时拥有狭窄和宽阔的T值,然后手动加宽 T如果那是我想要的。

无论如何,希望这再次对您有所帮助!

Link to code

关于typescript - 在 typescript 中使用带柯里化(Currying)的文字时如何防止缩小泛型类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57848957/

相关文章:

generics - 为什么编译器会将两个具有不同名称的等效签名的泛型类型变量识别为不同类型?

javascript - 使用 Typescript 的 Redux Thunk

java - JDK7 中的推断泛型和反引号

java - 不使用局部变量时无法推断Comparator的类型参数

swift - 为什么从reduce的返回值解构元组会导致错误?

rust - 意外的类型推断失败 : wrong number of type arguments

reactjs - 输入 mapStateToProps React Redux

node.js - 默认的 Firebase 应用不存在。确保在使用任何 Firebase 服务之前调用 initializeApp()。在 FirebaseAppError

Angular >2 Subject 直接订阅还是使用方法?

javascript - 排除 Nest.js 中的所有/api 路由以服务 React 应用程序