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;
该题目有两个要点:
处理输入是never导致返回never的情况
避免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
type IsUnion<T, K = T> = [T] extends [never] ? false : T extends any ? [K] extends [T] ? false : true : false
Solution by 359Steve #37075
type IsUnion
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-функции
type Helper<T, Copy = T>
T extends any
? дальнейшая логика
: never
Это выражение всегда будет верно, мы никогда не попадём в ветку со значением never, поэтому фактически там может быть всё, что угодно: false, 42, 'foo', не имеет значения. Обозначим её как never, чтобы наглядно это показать и перейдем к дальнейшей логике
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] |
Чему будет равен 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
Зная это, мы можем сравнить результат helper функции с boolean:
Equal<Helper<T>, boolean>
Helper<T> extends boolean будет означать что мы имеем дело с union типомtrue extends 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.
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