【问题标题】:typescript: Using infer with never get different result打字稿:使用推断永远不会得到不同的结果
【发布时间】:2021-06-02 19:09:04
【问题描述】:

我做了一些演示来重现我的问题。他们来了。

问题1:为什么输入g会得到2

// 1
type f = [never] extends [infer S1, ...infer S2]
  ? ([S1] extends [never] ? 1 : 2)
  : 3
// why 2
type g = [never] extends [infer S1, ...infer S2]
  ? ([never] extends [S1] ? 1 : 2)
  : 3

在我看来,输入f 等于g,因为S1never

问题2:为什么输入h 会得到2

// 1
type i = [never] extends [infer S1]
  ? ([never] extends [S1] ? 1 : 2)
  : 3
// why 2
type h = [never] extends [infer S1, ...infer S2]
  ? ([never] extends [S1] ? 1 : 2)
  : 3

在我看来,输入i 等于h,因为S2 未被使用。

问题3:为什么输入k会得到never

// 1
type j = [never] extends [infer S1, ...infer S2]
  ? (never extends S1 ? 1 : 2)
  : 3
// why never ?
type k = [never] extends [infer S1, ...infer S2]
  ? (S1 extends never ? 1 : 2)
  : 3

我什至无法理解。我认为k 至少应该是1 2 3 之一。

【问题讨论】:

  • 问题 1 和 2 对我来说似乎是 TypeScript 的错误,但在我有机会研究它之前我不确定。类型 3 是因为 S1 extends never ? 1 : 2distributive conditional type 并且 never 被视为“空联合”:如果 F<T | U> 始终等于 F<T> | F<U>,那么您可以证明 F<never> 应该是 never (或至少F<T> 的每个可能输出的子类型)。 See this answer.
  • 我仍然没有为#1 和#2 找到任何具体的东西。如果我没有找到任何东西,您可能想提交一个带有 this 之类的错误作为您的问题的 minimal reproducible example。看起来在 TS4.1 和 TS4.2 之间发生了什么。看起来它是 #42331 的变体,可能没有修复。

标签: typescript


【解决方案1】:

您的问题 1 和 2 似乎暴露了 TypeScript 中的错误。

我还希望您的 FGHI 类型(非原始类型通常以 UpperCamelCase 而不是 lowerCamelCase 编写,这使得它们看起来像变量名)评估为 @987654336 @。 GH 评估为 2 的事实令人惊讶。我的研究表明这种行为是新的。你可以验证你的代码works as expected in TypeScript 4.1.5,但是fails in TypeScript 4.2.2。具体来说,这一变化发生在 2021 年 1 月 12 日或 13 日前后。你的代码是works as expected in TypeScript 4.2.0-dev.20210112,但fails in TypeScript 4.2.0-dev.20210113

这种行为很可能是在 microsoft/TypeScript#42248 合并到主代码分支时引入的。此拉取请求修复了一个错误 (microsoft/TypeScript#39992),但似乎导致了一些新错误,例如 microsoft/TypeScript#42331

我已提交microsoft/TypeScrpit#43213 以跟踪此问题。现在我想说的是,如果你有一个真正的用例需要像上面的GH 那样构建,并且如果你不能等待这个问题得到解决,那么你可以通过使用输入别名而不是裸露的never,如下所示:

type N = never
type Gfixed = [N] extends [infer S1, ...infer S2] ? ([N] extends [S1] ? 1 : 2) : 3 // 1

您的问题 3 的行为符合预期。在K 类型中,(S1 extends never ? 1 : 2)distributive conditional type,因为被检查的类型S1 是一个类型参数。而这样的类型函数分布在联合上;这意味着如果F<T> 是可分配的,那么F<X | Y> 将等价于F<X> | F<Y>

这意味着F<never> 应该是never(或者至少它是F<T> 的所有可能答案的子类型)。为什么?因为X | never等价于X,所以F<X | never>必须等价于F<X> | F<never>。这意味着对于所有XF<X> | F<never> 等价于F<X>。那么,从概念上讲,F<never> 应该是 never

事实证明,当在分配条件类型中使用时,编译器实际上将never 视为"the empty union"。当我第一次遇到这种行为时,我绝对感到惊讶,因为将never 视为任何一种联合都需要一定的心态转变。

观察:

type Distributive<T> = T extends string ? { a: T } : { b: T };

type X = Distributive<"a"> // {a: "a"}
type Y = Distributive<0> // {b: 0}
type Z = Distributive<"a" | 0> // {a: "a"} | {b: 0}

type StillX = Distributive<"a" | never> // {a: "a"}
type Never = Distributive<never> // never

Playground link to code

【讨论】:

    猜你喜欢
    • 2016-08-31
    • 2023-01-18
    • 1970-01-01
    • 1970-01-01
    • 2021-11-09
    • 2020-08-27
    • 2019-07-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多