TypeScript 在使用Extract时,不正确的可分配性检查(TS2322)

sq1bmfud  于 9个月前  发布在  TypeScript
关注(0)|答案(5)|浏览(148)

Bug报告

🔎 搜索词

提取可分配的TS2322

🕗 版本与回归信息

这是我在每个版本中尝试的行为,我查阅了关于泛型、可分配性和提取的常见问题解答。
测试了4.2.3、4.3.5、4.4.4和4.5.0-dev.20211018版本。
在4.1.5版本中,使用类型别名的示例也失败了。从4.2.x开始,4.2版本引入了更智能的类型别名保留,这可能解释了为什么那个示例从4.2.x开始就通过了。

⏯ Playground链接

带有相关代码的Playground链接

💻 代码

我已经尽可能地最小化了这个示例。使用 Extract 来定义字段类型似乎很重要。

  1. type A<T> = {
  2. val: Extract<number | string, T>;
  3. };
  4. type A_number = A<number>;
  5. function f1(x: A_number): A<number| string> {
  6. // Passes type checking.
  7. return x;
  8. }
  9. function f2(x: A<number>): A<number | string> {
  10. // Fails type checking with error TS2322:
  11. // Type 'A<number>' is not assignable to type 'A<string | number>'.
  12. // Type 'string | number' is not assignable to type 'number'.
  13. // Type 'string' is not assignable to type 'number'.
  14. return x;
  15. }

🙁 实际行为

类型检查器报告错误TS2322:A<number> 不能分配给类型 A<string | number>,因为 string | number 不能分配给 number。它似乎是在错误的方向上检查分配性,因为分配性检查应该是 number 是否可以分配给 string | number
使用 A<number> 的类型别名不会产生相同的错误。我也无法在不使用 Extract 定义字段类型的情况下重现此错误。

🙂 预期行为

类型 A<number> 应该可以分配给类型 A<string | number>,因为它是一个简单的对象类型,而 number 可以分配给 string | number

xmd2e60i

xmd2e60i1#

A<T>T 中是逆变的,因为它是从另一种类型中减去 T 而不是直接使用它。因此,对于 T 的更宽类型将产生一个较窄的 A<T> ,所以这个错误是可以预料到的。

idfiyjo8

idfiyjo82#

A<T> 是对 T 的逆变,因为它是从另一种类型中减去 T 而不是将其原封不动地使用。因此,对于 T 的更宽类型将产生一个较窄的 A<T> ,所以这个错误是可以预料到的。

我没有看到它是如何从另一种类型中减去 T 的,因此是逆变的。Extract<T, U> 被定义为“从 T 中提取那些可以分配给 U 的类型”。如果 T 中的一个类型 S 可以分配给 U ,那么根据定义,S 也可以分配给 U | V 。在 A<T> 的情况下,我不认为扩大 T 会缩小 A<T> 的类型。执行类型替换应该使这一点变得清晰:

  1. type A<T> = {
  2. val: Extract<number | string, T>;
  3. };
  4. type An = A<number>; /*
  5. = { val: Extract<number | string, number>; };
  6. = { val: number; }; */
  7. type An = A<number | string>; /*
  8. = { val: Extract<number | string, number | string>; };
  9. = { val: number | string; }; */

最坏的情况下, A<T | V> 等于 A<T> (例如,数字和字符串都不能分配给 V )。

aoyhnmkz

aoyhnmkz3#

对不起,我的错误,我把 ExtractExclude 混淆了。话虽如此,问题/错误似乎是 TS 以某种原因测量 A<T> 作为逆变量:

  1. Type 'A<number>' is not assignable to type 'A<string | number>'.
  2. Type 'string | number' is not assignable to type 'number'.

string | number 的确不能分配给 number ,但这与 A 的方向相反。所以出于某种原因,它似乎认为 T 是逆变量的。🤔

jc3wubiy

jc3wubiy4#

它认为 A不变的 ;这也失败了:

  1. function f2(x: A<number | string>): A<number> {
  2. return x;
  3. }
  1. Type 'A<string | number>' is not assignable to type 'A<number>'.
  2. Types of property 'val' are incompatible.
  3. Type 'string | number' is not assignable to type 'number'.
  4. Type 'string' is not assignable to type 'number'.

这对于一般条件类型是有意义的(考虑 type X<T> = {} extends T ? 1 : 2 ; X<{}> 不能分配给或从 X<{ x: string }> ); Extract 只是特殊情况,其中它只具有协变性。
这里的真正错误是 TS 在这里进行协变性检查,而不是进行结构比较;协变性度量应该用 VarianceFlags.Unreliable 标记(这意味着协变性检查可能会产生假阴性,但不会产生假阳性)。

wxclj1h5

wxclj1h55#

可能相关的: #43887

相关问题