【问题标题】:Typescript doesn't infer correct type with Generic extending string union type?打字稿不能用通用扩展字符串联合类型推断出正确的类型?
【发布时间】:2020-05-28 22:35:54
【问题描述】:
type Test = 'a' | 'b' | 'c';

function foo<T extends Test>(arg: T) {
  if (arg === 'a') {
    console.log(arg);
  }
}

像这样.. 我希望 arg 在 if 块中推断为“a”。

但 ts 推断为只是 T。

为什么会出现这种情况?

我觉得extends这个关键字有点意思……

【问题讨论】:

  • 有趣。如果你写arg: string,那么它确实会正确缩小,所以它肯定与 T 是一个类型变量有关。但是如果你没有extends 子句那么它认为Tstring 没有重叠,所以没有办法测试它是否是由extends 引起的。这似乎是一个错误,但也许是有原因的。
  • 虽然问题本身可能很有趣,但上面示例中的泛型是无用的。只需使用function foo(arg: Test) ...
  • @AlekseyL。您可以假设这只是一个 minimal reproducible example 来演示问题,而不是实际用例。

标签: typescript typescript-typings


【解决方案1】:

问题是你的类型 T 不是 Test 而是一个子集,我们可以说每个可以用来代替 T 的类型,并且每个 for union 表示具有相同或更少的类型工会成员。考虑扩展 Test 的示例类型:

type Test = 'a' | 'b' | 'c';
type SubTest = 'b' | 'c'

type IsSubTest = SubTest extends Test ? true : false // evaluates to true

如您所见,SubTest 没有a 成员,但它可以分配给Test,因此我们可以将foo 函数用于此类类型。

const arg: SubTest = 'b'
foo(arg)

一切都好,没有错误。但这意味着您的条件arg === 'a' 永远不会满足,因为SubTest 中没有成员a,这就是为什么TS 不能假设在我们使用a 的条件内,因为在这种情况下这个分支完全无法访问,在里面输入never。我们甚至可以编写这样的函数并检查:

function foo(arg: SubTest) {
  if (arg === 'a') { // compilation error, condition will be always false
    console.log(arg);
  }
}

好的,但是即使 TS 没有缩小到 a,因为条件很明确,任何可以通过的都是 a,毫无疑问在这里!

让我们尝试手动重现存在于原始条件中的相同类型保护,并且条件检查左侧值 x(从 Test 扩展)是否等于右侧值说 y(从 Test 扩展)

function isTestMember<X extends Test, Y extends Test>(x: X, y: Y): x is Y { 
  return x == y  // error we cannot compare member of X with member of Y
}

如你所见,这样的 typeguard 甚至无法编写。由于 X 和 Y 不能重叠,因此条件 x === y 不适用于 XY 的所有成员

总而言之,TS 不能将不能重叠的类型的条件考虑为类型保护,因此类型不会缩小。

【讨论】:

  • 如果您定义function isTestMember&lt;Y extends Test&gt;(x: Test, y: Y): x is Y { return x == y },那么foo 函数工作正常,arg 被推断为T &amp; 'a' 块内的T &amp; 'a' 类型。所以我认为对于某些T 来说,条件并不总是可以满足这一事实并不重要。
  • 但是 x 不是 Test 而是 T extends Test
  • 那又怎样? x 可分配给Test,因为x: TT extends Test。仅仅因为T &amp; 'a' 可能不会被填充并不意味着我们不能在适当的类型保护之后将x 缩小到该类型;在这里,工作没有错误。 Playground Link
  • 在这种情况下,isTestMember 调用的返回类型被推断为x is 'a',并将x 缩小为类型T &amp; 'a',所以问题是为什么不arg === 'a'以同样的方式工作。
  • T &amp; 'a' 可以计算为never,当我们有像SubTest 这样的类型时,它将正好是neverT &amp; a 并不比 T 更好
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-02
  • 2022-01-05
  • 2017-12-24
  • 2019-04-10
  • 2018-04-28
相关资源
最近更新 更多