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

时间:2018-12-28 04:52:07

标签: typescript

是否可以检查给定类型是否为并集?

type IsUnion<T> = ???

为什么我需要这样做:在我的代码中,只有某些接收到的类型可以是联合的情况。我使用分布式条件类型来处理它。但是,对于查看此代码的人来说,为什么首先使用DCT并不明显。因此,我希望它是明确的,例如:IsUnion<T> 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 infer到U,但没有成功。

P.S。在某些人看来,我的用例可能并不完美/正确/良好,但是无论如何标题中的问题已经出现,我想知道是否有可能(我认为是可行的,但我自己很难弄清楚)。 / p>

2 个答案:

答案 0 :(得分:7)

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

这里是类型(感谢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 & B的事实。

Playground

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

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

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

答案 1 :(得分: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

注意事项

在某些情况下,这不起作用。由于成员boolean的分布,具有可分配给另一个成员的成员的任何联合将解析为T。可能最简单的例子是{}加入联合时,因为几乎所有东西(甚至是原语)都可以分配给它。您还将看到包含两种对象类型的并集,其中一种是另一种的子类型,即{ 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 & IsUnion<T>。或者,您可以仅使用T并将其命名为AssertUnion,这样,如果不是联合类型,整个类型就变成never。天空是极限。

感谢@ Gerrit0和@AnyhowStep在gitter上找到我的错误并提供了解决方法的反馈。

相关问题