注意: 这个答案是为了一个明确不想使用 UnionToIntersection
的情况而准备的。那个版本简单易懂,如果你对 U2I
没有任何疑虑,那就选择它吧。
我刚刚再次查看了这个问题,并在 @Gerrit0 的帮助下得出了以下结论:
type IsUnion<T, U extends T = T> =
T extends unknown ? [U] extends [T] ? false : true : false;
type Test = IsUnion<1 | 2>
type Test2 = IsUnion<1>
type Test3 = IsUnion<never>
看起来可能还可以进一步简化,我对此感到非常满意。这里的诀窍是分发 T
而不是 U
,以便您可以比较它们。因此,对于type X = 1 | 2
,您最终会检查[1 | 2] extends [1]
,这是错误的,因此此类型总体上为true
。如果T = never
,我们也会解析为false
(感谢 Gerrit)。
如果该类型不是联合类型,则T
和U
相同,所以此类型解析为false
。
注意事项
有些情况下,这种方法无法正常工作。任何具有可分配给另一个成员的成员的联合将解析为boolean
,因为T
的分发。可能最简单的例子是当{}
在联合中时,因为几乎所有内容(甚至原始数据)都可以赋值给它。在包含两个对象类型的联合中,您还将看到其中一个是另一个的子类型的情况,即{ 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发现我的错误并提供解决方法的反馈。
[T] extends [UnionToIntersection<T>]
不足以满足吗? - Titian Cernicova-DragomirU
被分配了。所以你在这里得到了基本上一个嵌套的for循环,而一些成员不扩展其他成员包括true
在输出中。当与自身进行比较时,成员将评估为false
。因此,对于联合,结果是true | false
,也就是boolean
。 - Keith Layne[T] extends [UnionToIntersection<T>]
的两侧都需要加上括号?T extends UnionToIntersection<T>
不行吗? - 3dGrabberT
,它将被Typescript“分发”。更多信息请参见:https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types https://dev59.com/Z1UK5IYBdhLWcg3wjwiY - Nurbol Alpysbayevstring | undefined
并不适用。 - rcbevans