diff --git a/README.md b/README.md index 0338004..51e1806 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ npm install --save-dev simplytyped **[Utils](#utils)** -[NoInfer](#noinfer) - [Nominal](#nominal) - [Nullable](#nullable) - [PromiseOr](#promiseor) +[NoInfer](#noinfer) - [Nominal](#nominal) - [Nullable](#nullable) - [PromiseOr](#promiseor) - [UnionToIntersection](#uniontointersection) **[Functions](#functions)** @@ -581,6 +581,30 @@ test('Will give back a promise containing given type union the type itself', t = ``` +### UnionToIntersection +Defines an intersection type of all union items. +```ts +test('Union of Strings', t => { + type got = UnionToIntersection<'hi' | 'there'>; + type expected = 'hi' & 'there'; + + assert(t); +}); + +test('Union of Objects', t => { + type got = UnionToIntersection<{ a: 0 } | { b: 1 } | { c: 2 }>; + + type expected = { + a: 0, + b: 1, + c: 2, + }; + + assert(t); +}); + +``` + ## Functions ### AnyFunc diff --git a/src/types/tuples.ts b/src/types/tuples.ts index 7783a66..43b33f0 100644 --- a/src/types/tuples.ts +++ b/src/types/tuples.ts @@ -1,3 +1,5 @@ +import {UnionToIntersection} from './utils'; + export interface Vector { readonly [x: number]: T; readonly length: number; } export type Length> = T['length']; @@ -9,18 +11,7 @@ export type Length> = T['length']; export type UnionizeTuple> = T[number]; /** * Gives an intersection of all values contained in a tuple. - * @param T a tuple of items up to 10 + * @param T a tuple of items * @returns an intersection of all items in the tuple */ -export type IntersectTuple> = - Length extends 1 ? T[0] : - Length extends 2 ? T[0] & T[1] : - Length extends 3 ? T[0] & T[1] & T[2] : - Length extends 4 ? T[0] & T[1] & T[2] & T[3] : - Length extends 5 ? T[0] & T[1] & T[2] & T[3] & T[4] : - Length extends 6 ? T[0] & T[1] & T[2] & T[3] & T[4] & T[5] : - Length extends 7 ? T[0] & T[1] & T[2] & T[3] & T[4] & T[5] & T[6] : - Length extends 8 ? T[0] & T[1] & T[2] & T[3] & T[4] & T[5] & T[6] & T[7] : - Length extends 9 ? T[0] & T[1] & T[2] & T[3] & T[4] & T[5] & T[6] & T[7] & T[8] : - Length extends 10 ? T[0] & T[1] & T[2] & T[3] & T[4] & T[5] & T[6] & T[7] & T[8] & T[9] : - any; +export type IntersectTuple>= UnionToIntersection; diff --git a/src/types/utils.ts b/src/types/utils.ts index b4e9244..a7b6db6 100644 --- a/src/types/utils.ts +++ b/src/types/utils.ts @@ -34,3 +34,13 @@ export type Nominal = T & Tagged; * @returns a the type union with a promise containing the given type */ export type PromiseOr = Promise | T; + + +/** + * Defines an intersection type of all union items. + * @param U Union of any types that will be intersected. + * @returns U items intersected + * @see https://stackoverflow.com/a/50375286/9259330 + */ +export type UnionToIntersection = + (U extends unknown ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never; diff --git a/test/utils/UnionToIntersection.test.ts b/test/utils/UnionToIntersection.test.ts new file mode 100644 index 0000000..03e645e --- /dev/null +++ b/test/utils/UnionToIntersection.test.ts @@ -0,0 +1,23 @@ +import test from 'ava'; +import { assert } from '../helpers/assert'; + +import { UnionToIntersection} from '../../src'; + +test('Union of Strings', t => { + type got = UnionToIntersection<'hi' | 'there'>; + type expected = 'hi' & 'there'; + + assert(t); +}); + +test('Union of Objects', t => { + type got = UnionToIntersection<{ a: 0 } | { b: 1 } | { c: 2 }>; + + type expected = { + a: 0, + b: 1, + c: 2, + }; + + assert(t); +});