打字稿:检查类型是不是为联合

Posted

技术标签:

【中文标题】打字稿:检查类型是不是为联合【英文标题】:Typescript: check if a type is a union打字稿:检查类型是否为联合 【发布时间】:2019-05-26 00:00:59 【问题描述】:

是否可以检查给定类型是否为联合?

type IsUnion<T> = ???

为什么我需要这个:在我的代码中,我只有一个接收到的类型可以是联合的情况。我用分布式条件类型处理它。但是,对于查看此代码的人来说,为什么首先使用 DCT 可能并不明显。所以我希望它是明确的:IsUnion&lt;T&gt; extends true ? T extends Foo ...

我用UnionToIntersection 做了几次尝试,但没有结果。我也想出了这个:

type IsUnion<T, U extends T = T> =
    T extends any ?
    (U extends T ? false : true)
    : never

它为非工会提供false,但由于某种原因,它为工会提供boolean......我不知道为什么。我还尝试从 T 发送 inferU,但没有成功。

附:我的用例在某些人看来可能不完美/正确/好,但无论如何标题中的问题已经出现,我想知道它是否可能(我觉得它是可能的,但我自己很难弄清楚)。

【问题讨论】:

【参考方案1】:

看来我自己想出了一个答案!

这是类型(感谢 Titian Cernicova-Dragomir 简化它!):

type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true

type Foo = IsUnion<'abc' | 'def'> // true
type Bar = IsUnion<'abc'> // false

jcalz 的UnionToIntersection 又派上了用场!

原理是基于联合A | B不扩展交叉A &amp; B

Playground

UPD。我很傻,没有将我的类型从问题发展成这个问题,这也很好:

type IsUnion<T, U extends T = T> =
    (T extends any ?
    (U extends T ? false : true)
        : never) extends false ? false : true

它将联合 TT 分配给成员,然后检查 U(联合)是否扩展了成员 T。如果是,那么它不是一个联合(但我仍然不知道为什么不添加extends false ? false : true就不起作用,即为什么前面的部分为联合返回boolean)。

【讨论】:

条件[T] extends [UnionToIntersection&lt;T&gt;] 不够吗? @TitianCernicova-Dragomir 哦,看来确实如此:D 我想我很愚蠢。我看了这个答案,但在我的脑海中,我是从头开始想出来的。现在我意识到它实际上是相同的。但是要回答为什么要分发额外的条件:U 的问题。因此,您在这里基本上得到了一个嵌套的 for 循环,并且某些成员不扩展其他成员的事实在输出中包括 true。当与自己比较时,成员将评估false。因此对于联合,结果是 true | false,又名 boolean【参考方案2】:

注意:此答案适用于某人明确不想使用UnionToIntersection 的情况。那个版本简单易懂,所以如果你对U2I没有疑虑,那就去吧。

我刚刚又看了一遍,在@Gerrit0 的帮助下想出了这个:

// Note: Don't pass U explicitly or this will break.  If you want, add a helper
// type to avoid that.
type IsUnion<T, U extends T = T> = 
  T extends unknown ? [U] extends [T] ? false : true : false;

type Test = IsUnion<1 | 2> // true
type Test2 = IsUnion<1> // false
type Test3 = IsUnion<never> // false

似乎可以进一步简化,对此我很满意。这里的诀窍是分发T 而不是U,以便您可以比较它们。所以对于type X = 1 | 2,你最终会检查[1 | 2] extends [1] 是否为假,所以这个类型总体上是true。如果T = never 我们也解析为false(感谢 Gerrit)。

如果类型不是联合,那么TU 是相同的,所以这个类型解析为false

注意事项

在某些情况下这不起作用。由于T 的分布,具有可分配给另一个成员的任何联合都将解析为boolean。可能最简单的例子是 在联合中,因为几乎所有东西(甚至是原语)都可以分配给它。您还将看到联合包括两种对象类型,其中一种是另一种的子类型,即 x: 1 | x: 1, y: 2

解决方法

    使用第三个 extends 子句(如 Nurbol 的回答)
(...) extends false ? false : true;
    使用never 作为错误案例:
T extends unknown ? [U] extends [T] ? never : true : never;
    在调用点反转extends
true extends IsUnion<T> ? Foo : Bar;
    由于您可能需要一个条件类型才能在调用站点使用它,因此将其包装起来:
type IfUnion<T, Yes, No> = true extends IsUnion<T> ? Yes : No;

根据您的需要,您可以使用此类型进行许多其他变体。一种想法是将unknown 用于肯定情况。然后你可以做T &amp; IsUnion&lt;T&gt;。或者您可以为此使用T 并将其称为AssertUnion,这样如果它不是联合类型,则整个类型将变为never。天空才是极限。

感谢 gitter 上的 @Gerrit0 和 @AnyhowStep 发现我的错误并就解决方法提供反馈。

【讨论】:

以上是关于打字稿:检查类型是不是为联合的主要内容,如果未能解决你的问题,请参考以下文章

打字稿:将标记的联合转换为联合类型

将打字稿接口属性类型转换为联合[重复]

函数的打字稿联合/交集类型

差异联合类型和区分联合打字稿/ F#

打字稿:对象和原语之间的keyof typeof联合总是永远不会

类型为 String 时如何在 DB 中查找项目 |使用打字稿联合类型的字符串 []