我在为 Sanctuary(用于函数式编程的 js 库)设计类型时遇到问题。我想创建 Ord
类型,代表具有自然顺序的任何值。 Ord
是:
- 一些内置的原始类型:
number
、string
、Date
、boolean
- 任何实现所需接口(interface)(
compareTo
方法)的用户类型 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
的上下文类型包括 string
、 number
和 boolean
):
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
希望对您有所帮助;祝你好运!
更新:如果您真的希望能够有选择地保留 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
除非手动指定。如果X
是never
, 的推断值 T
将被加宽。如果X
不是 never
, 指定的值将被使用而不是加宽。
这里的灵 active 值得奇怪笨重的杂耍吗?我想这取决于您自己的理解,但我可能会保留原始的 <T extends Ord>(x: T) => (y: T) => 0
如果我想灵活地同时拥有狭窄和宽阔的T
值,然后手动加宽 T
如果那是我想要的。
无论如何,希望这再次对您有所帮助!
关于typescript - 在 typescript 中使用带柯里化(Currying)的文字时如何防止缩小泛型类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57848957/