条件类型与泛型类型的推断方式不同
Posted
技术标签:
【中文标题】条件类型与泛型类型的推断方式不同【英文标题】:Conditional type is inferred differently with generic type 【发布时间】:2021-12-15 15:32:11 【问题描述】:Typescript 在传递泛型类型时以不同的方式推断条件类型(禁用分发)。
type Box<T> = a: T ;
// Conditional type with disabled distribution using tuple.
type CondType<T> = [T] extends [string | number]
? Box<T> : false;
class Test<T extends string | number>
public constructor(private value: T)
// CondType<T> is not inferred as Box<string | number>
public one(param: CondType<T>): void
param; // type is false | Box<T>
// This works as expected
public two(param: CondType<string | number>): void
param; // type is Box<string | number>
这是为什么?有没有办法解决它?
【问题讨论】:
这是github.com/microsoft/TypeScript/issues/23132 你可以使用param: CondType<T extend (string | number)>
【参考方案1】:
这是目前 TypeScript 中缺少的功能。有关相关功能请求,请参阅 microsoft/TypeScript#23132。它的状态是“等待更多反馈”,所以最好去那里,给它一个?,并描述你为什么需要它的用例,如果你认为它特别引人注目(它可能需要多一点不过,比这里的例子更有动力)。
问题是编译器在评估依赖于尚未指定的泛型类型参数的conditional types 时没有参考generic constraints。相反,编译器只是完全推迟评估。因此,即使T
被限制为string | number
,编译器也不会使用此信息急切地采用CondType<T>
的真正分支并导致Box<T>
。它保持为CondType<T>
,对于未指定的T
,未知可分配给Box<T>
。
除非解决 ms/TS#23132,否则可能的解决方法:
好吧,因为事实上CondType<T>
肯定会评估为Box<T>
,所以你可以改用那个类型:
public three(param: Box<T>): void
param; // Box<T> of course
虽然我想这可能不适用于您的实际用例(这就是为什么我会说 ms/TS#23132 中的任何评论都应该具有无法像这样简单解决的情况) .
对于您比编译器更了解某些表达式类型的情况,通用解决方法是使用a type assertion。从本质上讲,您正在从编译器中承担验证类型安全的工作,并将责任推给自己:
public four(param: CondType<T>): void
const paramAsserted = param as Box<T>; // just assert it
这会让你按照自己的意愿继续前进,尽管危险是你可能不小心对编译器撒了谎;假设您稍后将class Test<T extends string | number> /*...*/
更改为class Test<T extends string | number | boolean> /*...*/
。该类型断言将不再为真,但编译器不会报告错误。毕竟,编译器无法区分CondType<T>
何时可分配给通用Box<T>
的T
以及何时不可分配。所以你必须小心类型断言,并仔细检查你断言的内容是否正确。
Playground link to code
【讨论】:
奇怪的是,如果你删除条件类型中的元组包装,它似乎工作正常。以上是关于条件类型与泛型类型的推断方式不同的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin泛型总结 ★ ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 | 可变参数结合泛型 | out 协变 | in 逆变 | reified 检查泛型参数类型 )