【问题标题】:Merging type declarations with conditional types将类型声明与条件类型合并
【发布时间】:2019-05-13 04:31:35
【问题描述】:

当类型联合通过 TypeScript 中的条件类型传递时,删除条件时返回的值不一致。

当给定类型的联合时,typescript 正确地将可能的参数合并到每个参数中。

type Fn<T> = (arg: T) => void
Fn<string | number> // (arg: string | number) => void

但是,当混合使用条件参数时,参数将被解析

type ConditionalFn<T> = T extends never ? Fn<T> : Fn<T>
ConditionalFn<string | number> // Fn<string> | Fn<number>
// That is equivalent to which is ((arg: string) => void | (arg: number) => void)

通常这不是问题。但是当函数的类型设置为条件类型时,参数都设置为any,并且需要手动转换。

Playground Example

我希望ConditionalFn&lt;string | number&gt; 解析为Fn&lt;string&gt; | Fn&lt;number&gt;,然后进一步解析为Fn&lt;string | number&gt;


Follow up

为什么Improved behavior for calling union types 示例中的f 变量需要参数类型的交集? 我认为这是我困惑的根本原因。因为如果没有任何交集,它会强制参数类型为any

【问题讨论】:

  • “为什么f 变量需要参数类型的交集?”这个例子解释了它。这是参数类型逆变的结果。如果您认为函数的联合实际上是什么,您可以看到“或”(联合)如何切换到“和”(交集)。想象一对同卵双胞胎,Jen 和 Jan。我知道 Jen 喜欢吃各种蔬菜,我知道 Jan 喜欢吃任何有机食物。其中一个要来吃饭,但我不知道是哪一个。这将是 Jen Jan。我可以安全地喂我的神秘客人什么?
  • 我不能喂她传统的(非有机的)茄子,因为 Jan 可能不喜欢非有机的东西。我不能喂她有机牛排,因为如果是珍,她可能不喜欢肉。唯一安全的赌注是有机茄子……既是有机蔬菜的东西。函数的联合需要参数的交集。
  • 完美,这个描述很有道理。在我认为这些论点的结合应该奏效之前。但我现在明白了逆变。谢谢:)

标签: typescript


【解决方案1】:

您使用的条件类型是distributive,并按照您的预期将ConditionalFn&lt;string | number&gt; 扩展为Fn&lt;string&gt; | Fn&lt;number&gt;。但是Fn&lt;string&gt; | Fn&lt;number&gt; 不能分配给 Fn&lt;string | number&gt;。这些是完全不同的类型。

Fn&lt;string | number&gt; 是一种非常特殊的函数类型;一个可以同时接受stringnumber 参数。它是一个单独的函数,上面写着“我不在乎参数是string 还是number;我会接受其中任何一个”。

现在Fn&lt;string&gt; 类型的函数只需要接受stringFn&lt;number&gt; 类型的函数只需要接受numberFn&lt;string&gt; | Fn&lt;number&gt; 类型的函数是其中一个,我们只是不知道是哪一个。这是一个非常不具体/模糊的函数类型。因此很难真正调用这种类型的函数(在 TS3.3 之前,如果没有类型断言,你根本无法调用它),因为我可以自信地传递参数的唯一方法就是给它一些东西既是string,又是number;即string &amp; number。 TypeScript 3.3 添加了 support 用于调用具有交叉点的此类联合函数,这很棒......除了不存在 string &amp; number 类型的值;它相当于never。因此无法安全地调用 ConditionalFn&lt;string | number&gt; 类型的函数,这很可能是您的问题的原因。

重申这些类型之间的区别:函数(a: number) =&gt; console.log(2-a) 是有效的Fn&lt;string&gt; | Fn&lt;number&gt;(因为它是Fn&lt;number&gt;)但它不是是有效的Fn&lt;string | number&gt;(因为它不接受字符串)。

这确实会让人感到困惑,因为函数类型在其参数类型的相反方向上变窄/变宽。这称为contravariance,TypeScript 从 2.6 版开始支持它。

好的,希望对您有所帮助;祝你好运!

【讨论】:

  • 嗯,可能是我理解错误或解释错误。当参数类型重叠时,参数是 Fn&lt;string | number&gt; 的并集,但 Fn&lt;string&gt; | Fn&lt;number&gt; 的交集是预期的。但是没有任何重叠,ConditionalFn 似乎没有任何类型的函数参数。使用新的游乐场链接编辑了我的答案。
  • 是的,如果A &amp; Bnever,那么((x: A)=&gt;void) | ((x: B)=&gt;void) 不能安全地接受任何参数。我想我的回答涵盖了这一点,所以我不确定你在哪里仍然感到困惑。
猜你喜欢
  • 1970-01-01
  • 2017-09-10
  • 1970-01-01
  • 2022-08-12
  • 2011-08-25
  • 2013-05-02
  • 1970-01-01
  • 2018-10-21
  • 1970-01-01
相关资源
最近更新 更多