【问题标题】: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,但没有成功。

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

【问题讨论】:

    标签: typescript


    【解决方案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 }

    解决方法

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-05-24
      • 1970-01-01
      • 2019-11-05
      • 1970-01-01
      • 2017-09-16
      • 2017-05-18
      • 2018-11-03
      相关资源
      最近更新 更多