打字稿:检查类型是不是为联合
Posted
技术标签:
【中文标题】打字稿:检查类型是不是为联合【英文标题】:Typescript: check if a type is a union打字稿:检查类型是否为联合 【发布时间】:2019-05-26 00:00:59 【问题描述】:是否可以检查给定类型是否为联合?
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,但没有成功。
附:我的用例在某些人看来可能不完美/正确/好,但无论如何标题中的问题已经出现,我想知道它是否可能(我觉得它是可能的,但我自己很难弄清楚)。
【问题讨论】:
【参考方案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 & 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
)。
【讨论】:
条件[T] extends [UnionToIntersection<T>]
不够吗?
@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)。
如果类型不是联合,那么T
和U
是相同的,所以这个类型解析为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 & IsUnion<T>
。或者您可以为此使用T
并将其称为AssertUnion
,这样如果它不是联合类型,则整个类型将变为never
。天空才是极限。
感谢 gitter 上的 @Gerrit0 和 @AnyhowStep 发现我的错误并就解决方法提供反馈。
【讨论】:
以上是关于打字稿:检查类型是不是为联合的主要内容,如果未能解决你的问题,请参考以下文章