为啥泛型 T 仅在具有约束时才被推断为文字类型?
Posted
技术标签:
【中文标题】为啥泛型 T 仅在具有约束时才被推断为文字类型?【英文标题】:Why generic T is inferred as a literal type only when it has a constraint?为什么泛型 T 仅在具有约束时才被推断为文字类型? 【发布时间】:2019-04-06 06:27:59 【问题描述】:看下面的sn-p
declare function foo<T>(a: T): (b: T) => boolean;
foo(111)(222); // T inferred as 'number'
foo('hello')('bye'); // T inferred as 'string'
declare function bar<T extends number | string>(a: T): (b: T) => boolean;
bar(111)(222); // T inferred as '111'
bar('hello')('bye'); // T inferred as 'hello'
playground
如您所见,bar
函数将T
的类型推断为文字类型(示例中为'111'
和'hello'
),但在函数foo
中,它们被推断为number
或string
,唯一的区别就是约束。
奇怪的是,如果使用盒装类型如下
declare function baz<T extends Number | String>(a: T): (b: T) => boolean;
然后T
被推断为number
和string
,但其中一个是原始类型并且T
被推断为文字类型就足够了:
declare function brr<T extends Number | string>(a: T): (b: T) => boolean;
所以问题是: 为什么foo('hello')
将T
推断为string
而bar('hello')
将T
推断为'hello'
?为什么它只在T
受到约束时才会发生(至少在这个例子中)?
【问题讨论】:
当你说“为什么”时,你是在问背后的原因,还是在问 TypeScript 中的什么机制导致它发生? 顺便说一句,Number | string
推断数字文字可能是一个错误。似乎这种不一致的东西是“设计使然”的。
@PatrickRoberts 我在测试Number | String
时也有同样的想法,但是由于每个值都可以强制(在严格模式下)到包装的对象,然后是数字文字type 确实可以分配给“Number”。此外,这两种类型在函数内部将(大部分)无法区分。作为参考,这也适用于const n: Number = 42
。尽管如此,这似乎很奇怪。
【参考方案1】:
有时,您希望为字符串文字 'hello'
推断出精确的文字类型 'hello'
。有时,您希望为字符串文字 'hello'
推断出更广泛、非特定的 string
类型。
规则——什么时候应该推断出确切的类型,什么时候应该扩大类型——经历了几次迭代,当前的实现is presented here:
在调用表达式的类型实参推断期间,为类型参数 T 推断的类型扩大到其扩大的文字类型,如果:
对 T 的所有推断都是针对特定参数类型中 T 的***出现进行的,并且 T 没有约束或其约束不包括原始类型或文字类型,并且 T 在推理期间已修复,或者 T 未出现在返回类型的顶层。
【讨论】:
以上是关于为啥泛型 T 仅在具有约束时才被推断为文字类型?的主要内容,如果未能解决你的问题,请参考以下文章