01097-medium-isunion

Back

type IsUnion<T, U = T> =
  T extends any
    ? [U] extends [T]
      ? false
      : true
    : never;

T extends any is only to distribute union members so that it's possible to compare original union to a member of it. If it's not union [T] and [U] must be the same, otherwise, [U] would be different from [T]

Solution by daniel-hyun-chae #37930

type IsUnion<T, B = T> = 
  [T] extends [never] 
    ? false 
    : T extends T 
      ? ([B] extends [T] ? false : true)
      : never;

该题目有两个要点:

  1. 处理输入是never导致返回never的情况

  2. 避免B在内部判断触发分布式行为导致返回boolean

这两种情况也可以在除了判断never部分外再加一层判断来解决

type IsUnion<T, C extends T = T> = (
  T extends T ? (C extends T ? true : false) : never
) extends true
  ? false
  : true;

Solution by Barrenboat #37297

太抽象了

// your answers

Solution by djdidi #37170

type IsUnion<T, K = T> = [T] extends [never] ? false : T extends any ? [K] extends [T] ? false : true : false

Solution by 359Steve #37075

type IsUnion = keyof { [k in keyof T]: T[k] } extends T ? true: false

Solution by Gravity2333 #37064

// your answers
type IsUnion<T> = 
  [T] extends [never] ? false :
    Equal<[T], T extends unknown ? [T]: never> extends true ? false : true

Solution by duanlvxin #36726

// 你的答案
type IsUnion<T, U = T> = (T extends U ? U extends T ? false : true : never) extends false ? false : true;

Solution by aotushi #36113

type IsUnionImpl<T, B extends T = T> = (T extends T ? B extends T ? true : false : never) extends true ? false : true
type IsUnion<T> = IsUnionImpl<T>

Solution by vaclock #35846

type IsUnion<T, B = T> = [T] extends [never]
  ? false
  : T extends B
  ? [B] extends [T]
    ? false
    : true
  : never;

Solution by RanungPark #35574

Решение

type Helper<T, Copy = T> = 
  T extends any 
    ? Copy extends T
      ? true
      : false
    : never

type IsUnion<T> = Equal<Helper<T>, boolean>

Как это работает

Разберём строку за строкой, начиная с helper-функции

1. Создадим копию проверяемого типа для использования в двух вложенных циклах:

type Helper<T, Copy = T>

2. Запустим первый цикл проверяемого типа

T extends any
  ? дальнейшая логика
  : never

Это выражение всегда будет верно, мы никогда не попадём в ветку со значением never, поэтому фактически там может быть всё, что угодно: false, 42, 'foo', не имеет значения. Обозначим её как never, чтобы наглядно это показать и перейдем к дальнейшей логике

3. Второй цикл и сравнение

T extends any 
  ? Copy extends T
    ? true
    : false

Здесь происходит основной фокус решения. Чтобы понять наглядно, только для этого примера, давайте представим себе union типы в качестве массивов. Например, тип string | number как ['string', 'number'].

Эта конструкция запускает обход этих "массивов", сравнение их значений одного за другим и сохраняет результат в новый union тип ('массив'). Представьте себе это как некий аналог подобного javascript-кода:

const result = []
for (let k of ['string', 'number']) {
  for (let n of ['string', 'number']) {
    result.push(k === n)
  }

Результатом для нашего 'массива' будет [true, false, false, true], а последовательность обхода:

| Шаг № | k | n | result | |--------|--------|--------|--------| | 0 | string | string | [true] | | 1 | string | number | [true, false] | | 2 | number | string | [true, false, false] | | 3 | number | number | [true, false, false, true] |

4. Вернемся от нашей аналогии с массивом к настоящему результату - union типу

Чему будет равен union true | false | false | true ? Правильно - true | false А чему будет равен юнион true | false ? Верно - boolean

Идея в том, что для любого union типа при двойном обходе результатом всегда будет boolean, потому что невозможно сравнить два и более элемента друг с другом и получить только true, это всегда будет boolean (true | false) - type 1 === type 1 | type1 === type2

А для любого другого типа результатом всегда будет только true - singleType === singleType

5. Финал

Зная это, мы можем сравнить результат helper функции с boolean: Equal<Helper<T>, boolean>

Solution by alex-altay #34845

// your answers
type IsUnion<T, U = T> = [T] extends [never]
? false
: T extends unknown
 ? [U] extends [T]
  ? false
  : true
 : never;

Solution by AndreGeng #34750

type IsUnion<T, C = T> = (T extends T ? C extends T ? true : unknown : never) extends true ? false : true;

Solution by devshinthant #34624

// solution 1 
type IsUnion<T, B = T> = [T] extends [never] ? false :
  T extends B ? [B] extends [T] ? false : true : never

// solution 2 
type IsUnion<T, B = T> =
  (T extends T ? B extends T ? true : unknown : never) extends true ? false : true

Solution by Jayce-liang #34338

type IsUnion<T, U = T> = [T] extends [never] ? false : T extends U ? [U] extends [T] ? false : true : never 

Solution by ouzexi #34029

// your answers
type IsUnion<T, U = T> =[T] extends [never] ? false :
 T extends T ? [T, U] extends [U, T] ? false : true : false

Solution by heyuelan #33897

// 你的答案
type IsUnion<T, U = T> = [T] extends [never] ? false : T extends U ? [U] extends [T] ? false:  true : false;

Solution by HelloGGG #33423

// your answers
type IsUnion<T, K = T> = [T] extends [never]
  ? false
  : K extends K
    ? [T] extends [K]
      ? false
      : true
    : never

Solution by pea-sys #32610

type IsUnion<T, C = T> = 
  (T extends unknown 
    ? C extends T 
      ? true
      : false
    : never) extends true ? false : true

분배법칙이 일어났을 때의 T와 정적으로 C가 가지고 있는 타입의 관계를 비교하여 Union인지 아닌지를 체크할 수 있다.

Solution by dev-hobin #32422

type IsUnion<T, K = T> = [T] extends [never]
  ? false
  : K extends K
    ? [T] extends [K]
      ? false
      : true
    : never

Solution by yoonnokdoo #32395

type IsUnion<T, S = T> = [T] extends [never]
  ? false
  : S extends S
    ? Equal<T, S> extends true
      ? false
      : true
    : never
type IsUnion<T, S = T> = [T] extends [never]
  ? false
  : S extends S
    ? NotEqual<T, S>
    : never

Solution by Heonys #32100

// your answers
// 使用 296 的 Permutation,得出笛卡尔乘积组合解决
type Permutation<T, U extends T = T> = [T] extends [never]
  ? []
  : U extends U
  ? [U, ...Permutation<Exclude<T, U>>]
  : never;
type IsUnion<T> = Permutation<T>['length'] extends (0 | 1) ? false : true;

Solution by wenxiangdong #31760

type IsUnion<T, U = T> = [T] extends [never] 
  ? false 
  : T extends infer V 
    ? [Exclude<U, V>] extends [never] ? false : true
    : never

Solution by kai-phan #31667


type IsUnion<T> = { [K in T as T extends K ? 'false' : 'true']: true } extends { true: true } ? true : false;

Basic idea: match every member in a union against everything and map this to a mapped type with "true"/"false" keys, then try to match for "true" key.

PS: I'm not sure if I used the correct language or if I properly understood how everything works.

Solution by tany1 #31604

type IsUnion<T, B = T> = [T] extends [never]
  ? false
  : T extends B
  ? [B] extends [T]
    ? false
    : true
  : never;

Solution by vipulpathak113 #31538

With NotEqual.

// your answers
type IsUnion<T> = NotEqual<[T] & T, (T extends T ? [T] : never) & T>; 

Solution by sugoroku-y #31477

Referenced Equal.

// your answers
type IsUnion<T> = (
    T extends T ? <S>() => S extends T ? 1 : 2 : never
) extends <S>() => S extends T ? 1 : 2
    ? false
    : true;

@doox911-opensource commented

I think this is the best solution. And great explanation.

But the test:

  Expect<Equal<IsUnion<(() => any)|(() => 15)>, true >>,

failed

This test case has also been successful.

Playground

Solution by sugoroku-y #31295

type IsUnionImpl<T, C extends T = T> = (T extends T ? C extends T ? true : unknown : never) extends true ? false : true;
type IsUnion<T> = IsUnionImpl<T>;

Solution by MyeonghoonNam #30992

type IsUnion<T, K = T> = 
  [T] extends [never]
    ? false
    : K extends K
      ? [Exclude<T, K>] extends [never]
        ? false
        : true
      : false;

Solution by vprokashev #30955

// your answers
type UnionToArray<T> = T extends T ? Array<T> : never
type IsUnion<T> = [T] extends [never] ? false : Array<T> extends UnionToArray<T> ? false : true

Solution by yangdonglai #30722

type IsUnion<T, R = T> = [T] extends [never] ? false : (T extends any ? ([Exclude<R, T>] extends [never] ? false : true) : true);

Solution by kai-phan #30391