条件类型与泛型类型的推断方式不同

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&lt;T extend (string | number)&gt; 【参考方案1】:

这是目前 TypeScript 中缺少的功能。有关相关功能请求,请参阅 microsoft/TypeScript#23132。它的状态是“等待更多反馈”,所以最好去那里,给它一个?,并描述你为什么需要它的用例,如果你认为它特别引人注目(它可能需要多一点不过,比这里的例子更有动力)。

问题是编译器在评估依赖于尚未指定的泛型类型参数的conditional types 时没有参考generic constraints。相反,编译器只是完全推迟评估。因此,即使T 被限制为string | number,编译器也不会使用此信息急切地采用CondType&lt;T&gt; 的真正分支并导致Box&lt;T&gt;。它保持为CondType&lt;T&gt;,对于未指定的T,未知可分配给Box&lt;T&gt;


除非解决 ms/TS#23132,否则可能的解决方法:

好吧,因为事实上CondType&lt;T&gt; 肯定会评估为Box&lt;T&gt;,所以你可以改用那个类型:

  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&lt;T extends string | number&gt; /*...*/ 更改为class Test&lt;T extends string | number | boolean&gt; /*...*/。该类型断言将不再为真,但编译器不会报告错误。毕竟,编译器无法区分CondType&lt;T&gt; 何时可分配给通用Box&lt;T&gt;T 以及何时不可分配。所以你必须小心类型断言,并仔细检查你断言的内容是否正确。

Playground link to code

【讨论】:

奇怪的是,如果你删除条件类型中的元组包装,它似乎工作正常。

以上是关于条件类型与泛型类型的推断方式不同的主要内容,如果未能解决你的问题,请参考以下文章

模板与泛型编程——模板实参推断

c# 泛型学习

泛型

java泛型

泛型与枚举

Kotlin泛型总结 ★ ( 泛型类 | 泛型参数 | 泛型函数 | 多泛型参数 | 泛型类型约束 | 可变参数结合泛型 | out 协变 | in 逆变 | reified 检查泛型参数类型 )