TypeScript 协方差在检查类型中的未定义时中断,

pcww981p  于 9个月前  发布在  TypeScript
关注(0)|答案(3)|浏览(101)

Bug报告

我正在使用 solid-js npm 包,它定义了一个名为 Setter 的类型:

  1. type Setter<T> = (undefined extends T ? () => undefined : {}) & (<U extends T>(value: (prev: T) => U) => U) & (<U extends T>(value: Exclude<U, Function>) => U) & (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);

我不太理解其中的逻辑,但它给我带来了一些问题:

  1. type Setter<T> = (undefined extends T ? () => undefined : {}) & (<U extends T>(value: (prev: T) => U) => U) & (<U extends T>(value: Exclude<U, Function>) => U) & (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);
  2. class A { a = 1 }
  3. class B extends A { b = 2 }
  4. type Test<T> = { 0: Setter<T> };
  5. declare const a: Test<A>;
  6. const b: Test<B> = a; // ERROR!

我创建了一个具有属性 0 的对象,该属性包含一个 Setter,似乎它不是协变的。
检查错误时,看起来问题起源于这部分 undefined extends T ? () => undefined : {},实际上,如果我删除它,这就可以工作了,而且即使这是类型的唯一部分,问题仍然会发生:

  1. type Setter<T> = undefined extends T ? () => undefined : {};
  2. class A { a = 1 }
  3. class B extends A { b = 2 }
  4. type Test<T> = { 0: Setter<T> };
  5. declare const a: Test<A>;
  6. const b: Test<B> = a; // ERROR!

我认为这是一个错误,因为类型是相同的:

  1. type TA = Test<A>;
  2. // ^? type TA = { 0: {}; }
  3. type TB = Test<B>;
  4. // ^? type TB = { 0: {}; }

此外,如果将 Test 定义为 [ Setter<T> ],则不会发生错误:

  1. type Setter<T> = undefined extends T ? () => undefined : {};
  2. class A { a = 1 }
  3. class B extends A { b = 2 }
  4. type Test<T> = [ Setter<T> ];
  5. declare const a: Test<A>;
  6. const b: Test<B> = a; // OK?!?!

🔎 搜索词

  • undefined extends T
  • 协变性
  • 协变

🕗 版本与回归信息

此更改发生在版本 3.3.33.5.1 之间。(在所有版本中都不会发生的唯一Playground版本是 3.3.3)

⏯ Playground链接

Playground链接

💻 代码

  1. type Setter<T> = (undefined extends T ? () => undefined : {}) & (<U extends T>(value: (prev: T) => U) => U) & (<U extends T>(value: Exclude<U, Function>) => U) & (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);
  2. // type Setter<T> = (<U extends T>(value: (prev: T) => U) => U) & (<U extends T>(value: Exclude<U, Function>) => U) & (<U extends T>(value: Exclude<U, Function> | ((prev: T) => U)) => U);
  3. // type Setter<T> = undefined extends T ? () => undefined : {};
  4. class A { a = 1 }
  5. class B extends A { b = 2 }
  6. type Test<T> = { 0: Setter<T> };
  7. // type Test<T> = [ Setter<T> ];
  8. declare const a: Test<A>;
  9. const b: Test<B> = a; // ERROR!
  10. type TA = Test<A>;
  11. // ^?
  12. type TB = Test<B>;
  13. // ^?

🙁 实际行为

无法将 b 的值分配给 a
至少它是错误的,因为它不一致。

🙂 预期行为

可以将 b 的值分配给 a

6g8kf2rb

6g8kf2rb1#

我相信这里的错误是我们没有将 U extends T 标记为在 T 上不可测量的方差计算。

  1. // Measured as invariant (wrong)
  2. type IsSupertypeOfString<T> = string extends T ? true : false;
  3. // Covariant use of invariant type is still invariant
  4. type BoxOfIsSupertypeOfString<T> = { readonly value: IsSupertypeOfString<T> };
  5. declare let direct_number: IsSupertypeOfString<number>;
  6. declare let direct_boolean: IsSupertypeOfString<boolean>;
  7. // OK (non-variance-based relation)
  8. direct_number = direct_boolean;
  9. declare let box_number: BoxOfIsSupertypeOfString<number>;
  10. declare let box_boolean: BoxOfIsSupertypeOfString<boolean>;
  11. // Error (variance-based relation)
  12. box_number = box_boolean;

cc @ahejlsberg

展开查看全部
jaxagkaj

jaxagkaj2#

我之前以为我已经有一个PR来修复这个问题了。:P

svmlkihl

svmlkihl3#

由于#43887从未被合并,#54866是否会解决这个问题?

相关问题