type-challenges:IsUnion

93 阅读1分钟

IsUnion

问题描述

实现一个类型 IsUnion,该类型接受输入类型 T 并返回 T是否解析为联合类型。

举例:

type case1 = IsUnion<string> // false
type case2 = IsUnion<string | number> // true
type case3 = IsUnion<[string | number]> // false
// ============= Test Cases =============
import type { Equal, Expect } from './test-utils'type cases = [
  Expect<Equal<IsUnion<string>, false>>,
  Expect<Equal<IsUnion<string | number>, true>>,
  Expect<Equal<IsUnion<'a' | 'b' | 'c' | 'd'>, true>>,
  Expect<Equal<IsUnion<undefined | null | void | ''>, true>>,
  Expect<Equal<IsUnion<{ a: string } | { a: number }>, true>>,
  Expect<Equal<IsUnion<{ a: string | number }>, false>>,
  Expect<Equal<IsUnion<[string | number]>, false>>,
  // Cases where T resolves to a non-union type.
  Expect<Equal<IsUnion<string | never>, false>>,
  Expect<Equal<IsUnion<string | unknown>, false>>,
  Expect<Equal<IsUnion<string | any>, false>>,
  Expect<Equal<IsUnion<string | 'a'>, false>>,
  Expect<Equal<IsUnion<never>, false>>
]
​
// ============= Your Code Here =============
type IsUnion<T, C extends T = T> = (T extends T ? (C extends T ? true : unknown) : never) extends true ? false : true
  1. 外层条件类型: (T extends T ? (C extends T ? true : unknown) : never) extends true

    • T extends T ? ... : ... 是一个分布式条件类型,当 T 是联合类型时,这个条件会对联合类型的每个成员进行检查。
    • 假设 T = A | B,那么 (A | B) extends (A | B) 会被分解成 A extends (A | B) ? (B extends (A | B) ? true : unknown) : neverB extends (A | B) ? (A extends (A | B) ? true : unknown) : never
    • 如果 T 不是联合类型(例如是单个类型),那么 T 只有一个成员,T extends T => true
  2. 内层条件类型: (C extends T ? true : unknown)

    • CT 的一个扩展类型,并且默认为 T 本身。因为 C 也是 T 的成员之一,所以这一步也会对联合类型的每个成员进行检查。
    • 对于联合类型的每个成员,检查 C extends T ? true : unknown 会产生 trueunknown, 最终结果 (true | unknown) 不能扩展 true
    • 如果 T 不是联合类型(例如是单个类型),那么 T 只有一个成员, C extends T => true
  • 因此,如果 T 是联合类型,上述外层条件的结果将是 true,反之是 false

最终,这个类型的判断逻辑如下:

  • 如果结果为 true,表示 T 不是联合类型。
  • 如果结果不是 true,表示 T 是联合类型。