From e9a67b0f0211a11158e954cb7b9ac097dd205db8 Mon Sep 17 00:00:00 2001 From: haejunejung Date: Tue, 24 Sep 2024 14:41:23 +0900 Subject: [PATCH 1/3] feat(UnionToIntersection): Add UnionToIntersection type --- source/utilities/UnionToIntersection.d.ts | 28 +++++++++++++++++++++++ source/utilities/index.ts | 1 + 2 files changed, 29 insertions(+) create mode 100644 source/utilities/UnionToIntersection.d.ts diff --git a/source/utilities/UnionToIntersection.d.ts b/source/utilities/UnionToIntersection.d.ts new file mode 100644 index 0000000..c3710bd --- /dev/null +++ b/source/utilities/UnionToIntersection.d.ts @@ -0,0 +1,28 @@ +import { IsNever } from '@/predicate/IsNever'; +import { Simplify } from './Simplify'; + +type InternalUnionToIntersection = ( + U extends unknown ? (x: U) => void : never +) extends (x: infer I) => void + ? I & U + : never; + +type SimplifyIfRecord = + T extends Record ? Simplify : T; + +/** + * @description Convert a union type to an intersection type using distributive conditional types. + * + * @template U The union type to be converted to an intersection type. + * + * @returns The intersection type resulting from the conversion of the union type. + * + * @example + * type T0 = { a: number }; + * type T1 = { b: string }; + * type Result = UnionToIntersection; // { a: number; b: string; } + */ +export type UnionToIntersection = + IsNever extends true + ? never + : SimplifyIfRecord>; diff --git a/source/utilities/index.ts b/source/utilities/index.ts index ced718a..aa25027 100644 --- a/source/utilities/index.ts +++ b/source/utilities/index.ts @@ -8,3 +8,4 @@ export type { StrictExclude } from './StrictExclude'; export type { StrictExtract } from './StrictExtract'; export type { StrictOmit } from './StrictOmit'; export type { TupleToUnion } from './TupleToUnion'; +export type { UnionToIntersection } from './UnionToIntersection'; From 6713307769f0697f2214cba365e3d4d17595af78 Mon Sep 17 00:00:00 2001 From: haejunejung Date: Tue, 24 Sep 2024 14:41:43 +0900 Subject: [PATCH 2/3] test(UnionToIntersection): Add UnionToIntersection test --- .../utilities/UnionToIntersection.test-d.ts | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 test-d/utilities/UnionToIntersection.test-d.ts diff --git a/test-d/utilities/UnionToIntersection.test-d.ts b/test-d/utilities/UnionToIntersection.test-d.ts new file mode 100644 index 0000000..de98991 --- /dev/null +++ b/test-d/utilities/UnionToIntersection.test-d.ts @@ -0,0 +1,46 @@ +import { UnionToIntersection } from '@/utilities'; +import { expectNever, expectType } from 'tsd'; + +declare function unionToIntersection(): UnionToIntersection; + +// T0: Object literal union +type T0 = { a: number } | { b: string }; +expectType<{ a: number; b: string }>(unionToIntersection()); + +// T1: Union of object literal and simple type +type T1 = { a: number } | 'A'; +expectType<{ a: number } & 'A'>(unionToIntersection()); + +// T2: Combination of object literal, function, and tuple +type T2 = { a: number } | (() => void) | [number]; +expectType<{ a: number } & (() => void) & [number]>(unionToIntersection()); + +// T3: Union of function types +type T3 = (() => string) | ((x: number) => number); +expectType<(() => string) & ((x: number) => number)>(unionToIntersection()); + +// T4: Union of tuple types +type T4 = [number] | [string]; +expectType<[number] & [string]>(unionToIntersection()); + +// T5: Nested object literal +type T5 = { a: { b: string } } | { a: { c: number } }; +expectType<{ + a: { + b: string; + } & { + c: number; + }; +}>(unionToIntersection()); + +// T6: Union of object literal and tuple +type T6 = { a: number } | [string]; +expectType<{ a: number } & [string]>(unionToIntersection()); + +// T7: Combination of object literal, primitive type, and tuple +type T7 = { a: string } | 'B' | [number]; +expectType<{ a: string } & 'B' & [number]>(unionToIntersection()); + +// T8: Simple union type (not an object) +type T8 = 'A' | 'B' | 'C' | 'D' | 'E'; +expectNever(unionToIntersection()); From 6921bac67e2ddac99388f3aaf0643b0b4d5b308f Mon Sep 17 00:00:00 2001 From: haejunejung Date: Tue, 24 Sep 2024 14:49:52 +0900 Subject: [PATCH 3/3] docs(UnionToIntersection): Add UnionToIntersection docs --- docs/.vitepress/en.mts | 4 ++ docs/.vitepress/ko.mts | 4 ++ .../utilities/UnionToIntersection.md | 65 +++++++++++++++++++ .../utilities/UnionToIntersection.md | 65 +++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 docs/ko/reference/utilities/UnionToIntersection.md create mode 100644 docs/reference/utilities/UnionToIntersection.md diff --git a/docs/.vitepress/en.mts b/docs/.vitepress/en.mts index c851bfe..188904c 100644 --- a/docs/.vitepress/en.mts +++ b/docs/.vitepress/en.mts @@ -135,6 +135,10 @@ export default defineConfig({ text: 'TupleToUnion', link: '/reference/utilities/TupleToUnion', }, + { + text: 'UnionToIntersection', + link: '/reference/utilities/UnionToIntersection', + }, ], }, ], diff --git a/docs/.vitepress/ko.mts b/docs/.vitepress/ko.mts index e604cd3..820b254 100644 --- a/docs/.vitepress/ko.mts +++ b/docs/.vitepress/ko.mts @@ -141,6 +141,10 @@ export default defineConfig({ text: 'TupleToUnion', link: '/ko/reference/utilities/TupleToUnion', }, + { + text: 'UnionToIntersection', + link: '/ko/reference/utilities/UnionToIntersection', + }, ], }, ], diff --git a/docs/ko/reference/utilities/UnionToIntersection.md b/docs/ko/reference/utilities/UnionToIntersection.md new file mode 100644 index 0000000..194844c --- /dev/null +++ b/docs/ko/reference/utilities/UnionToIntersection.md @@ -0,0 +1,65 @@ +# UnionToIntersection\ + +## 개요 + +분배 조건부 타입을 사용하여 유니온 타입을 교차 타입으로 변환해요. + +## 문법 + +```ts +type InternalUnionToIntersection = ( + U extends unknown ? (x: U) => void : never +) extends (x: inter I) => void + ? I & U + : never; + +type SimplifyIfRecord = + T extends Record ? Simplify : T; + +type UnionToIntersection = + IsNever extends true + ? never + : SimplifyIfRecord>; +``` + +- **U**: 교차 타입으로 변환될 유니온 타입이에요. + +## 예제 + +```ts +// T0: Object literal union +type T0 = { a: number } | { b: string }; +type E0 = UnionToIntersection; // { a: number; b: string; } + +// T1: Union of object literal and simple type +type T1 = { a: number } | 'A'; +type E1 = UnionToIntersection; // { a: number } & 'A' + +// T2: Combination of object literal, function, and tuple +type T2 = { a: number } | (() => void) | [number]; +type E2 = UnionToIntersection; // { a: number } & (() => void) & [number] + +// T3: Union of function types +type T3 = (() => string) | ((x: number) => number); +type E3 = UnionToIntersection; // (() => string) & ((x: number) => number) + +// T4: Union of tuple types +type T4 = [number] | [string]; +type E4 = UnionToIntersection; // [number] & [string] + +// T5: Nested object literal +type T5 = { a: { b: string } } | { a: { c: number } }; +type E5 = UnionToIntersection; // { a: { b: string } & { c: number } } + +// T6: Union of object literal and tuple +type T6 = { a: number } | [string]; +type E6 = UnionToIntersection; // { a: number } & [string] + +// T7: Combination of object literal, primitive type, and tuple +type T7 = { a: string } | 'B' | [number]; +type E7 = UnionToIntersection; // { a: string } & 'B' & [number] + +// T8: Simple union type (not an object) +type T8 = 'A' | 'B' | 'C' | 'D' | 'E'; +type E8 = UnionToIntersection; // never +``` diff --git a/docs/reference/utilities/UnionToIntersection.md b/docs/reference/utilities/UnionToIntersection.md new file mode 100644 index 0000000..741b70d --- /dev/null +++ b/docs/reference/utilities/UnionToIntersection.md @@ -0,0 +1,65 @@ +# UnionToIntersection\ + +## Overview + +Convert a union type to an intersection type using distributive conditional types. + +## Syntax + +```ts +type InternalUnionToIntersection = ( + U extends unknown ? (x: U) => void : never +) extends (x: inter I) => void + ? I & U + : never; + +type SimplifyIfRecord = + T extends Record ? Simplify : T; + +type UnionToIntersection = + IsNever extends true + ? never + : SimplifyIfRecord>; +``` + +- **U**: The union type to be converted to an intersection type. + +## Example + +```ts +// T0: Object literal union +type T0 = { a: number } | { b: string }; +type E0 = UnionToIntersection; // { a: number; b: string; } + +// T1: Union of object literal and simple type +type T1 = { a: number } | 'A'; +type E1 = UnionToIntersection; // { a: number } & 'A' + +// T2: Combination of object literal, function, and tuple +type T2 = { a: number } | (() => void) | [number]; +type E2 = UnionToIntersection; // { a: number } & (() => void) & [number] + +// T3: Union of function types +type T3 = (() => string) | ((x: number) => number); +type E3 = UnionToIntersection; // (() => string) & ((x: number) => number) + +// T4: Union of tuple types +type T4 = [number] | [string]; +type E4 = UnionToIntersection; // [number] & [string] + +// T5: Nested object literal +type T5 = { a: { b: string } } | { a: { c: number } }; +type E5 = UnionToIntersection; // { a: { b: string } & { c: number } } + +// T6: Union of object literal and tuple +type T6 = { a: number } | [string]; +type E6 = UnionToIntersection; // { a: number } & [string] + +// T7: Combination of object literal, primitive type, and tuple +type T7 = { a: string } | 'B' | [number]; +type E7 = UnionToIntersection; // { a: string } & 'B' & [number] + +// T8: Simple union type (not an object) +type T8 = 'A' | 'B' | 'C' | 'D' | 'E'; +type E8 = UnionToIntersection; // never +```