Skip to content

Commit

Permalink
[Feature] Add UnionToIntersection type (#109)
Browse files Browse the repository at this point in the history
Resolves: #49
  • Loading branch information
dosentmatter authored and piotrwitek committed Oct 22, 2019
1 parent d935928 commit 5b7cd0c
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 0 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Issues can be funded by anyone and the money will be transparently distributed t
* [`PromiseType<T>`](#promisetypet)
* [`Unionize<T>`](#unionizet)
* [`Brand<T, U>`](#brandt-u)
* [`UnionToIntersection<U>`](#uniontointersectionu)

## Flow's Utility Types

Expand Down Expand Up @@ -832,6 +833,21 @@ gross(eur); // Type '"EUR"' is not assignable to type '"USD"'.

[⇧ back to top](#flows-utility-types)

### `UnionToIntersection<U>`

Get intersection type given union type `U`

**Usage:**

```ts
import { UnionToIntersection } from 'utility-types';

// Expect: { name: string } & { age: number } & { visible: boolean }
UnionToIntersection<{ name: string } | { age: number } | { visible: boolean }>
```

[⇧ back to top](#table-of-contents)

---

## Flow's Utility Types
Expand Down
16 changes: 16 additions & 0 deletions src/__snapshots__/mapped-types.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,22 @@ exports[`Subtract testType<Subtract<Props, DefaultProps>>() 1`] = `"Pick<Props,
exports[`SymmetricDifference testType<SymmetricDifference<'1' | '2' | '3', '2' | '3' | '4'>>() 1`] = `"\\"1\\" | \\"4\\""`;
exports[`UnionToIntersection testType<
UnionToIntersection<
{ name: string } | { age: number } | { visible: boolean }
>
>({
name: 'Yolo',
age: 99,
visible: true,
}) 1`] = `"{ name: string; } & { age: number; } & { visible: boolean; }"`;
exports[`UnionToIntersection testType<UnionToIntersection<'name' | 'age'>>() 1`] = `"\\"name\\" & \\"age\\""`;
exports[`UnionToIntersection testType<UnionToIntersection<boolean>>() 1`] = `"false & true"`;
exports[`UnionToIntersection testType<UnionToIntersection<true | false>>() 1`] = `"false & true"`;
exports[`Unionize testType<Unionize<Props>>() 1`] = `"{ name: string; } | { age: number; } | { visible: boolean; }"`;
exports[`ValuesType testType<ValuesType<[1, 2]>>() 1`] = `"1 | 2"`;
Expand Down
1 change: 1 addition & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export {
Subtract,
SymmetricDifference,
Unionize,
UnionToIntersection,
ValuesType,
WritableKeys,
} from './mapped-types';
Expand Down
24 changes: 24 additions & 0 deletions src/mapped-types.spec.snap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
Optional,
ValuesType,
AugmentedRequired,
UnionToIntersection,
} from './mapped-types';

/**
Expand Down Expand Up @@ -540,3 +541,26 @@ type RequiredOptionalProps = {
visible: true,
});
}

// @dts-jest:group UnionToIntersection
{
// @dts-jest:pass:snap -> { name: string; } & { age: number; } & { visible: boolean; }
testType<
UnionToIntersection<
{ name: string } | { age: number } | { visible: boolean }
>
>({
name: 'Yolo',
age: 99,
visible: true,
});

// @dts-jest:pass:snap -> false & true
testType<UnionToIntersection<boolean>>();

// @dts-jest:pass:snap -> false & true
testType<UnionToIntersection<true | false>>();

// @dts-jest:pass:snap -> "name" & "age"
testType<UnionToIntersection<'name' | 'age'>>();
}
24 changes: 24 additions & 0 deletions src/mapped-types.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
Optional,
ValuesType,
AugmentedRequired,
UnionToIntersection,
} from './mapped-types';

/**
Expand Down Expand Up @@ -540,3 +541,26 @@ type RequiredOptionalProps = {
visible: true,
});
}

// @dts-jest:group UnionToIntersection
{
// @dts-jest:pass:snap
testType<
UnionToIntersection<
{ name: string } | { age: number } | { visible: boolean }
>
>({
name: 'Yolo',
age: 99,
visible: true,
});

// @dts-jest:pass:snap
testType<UnionToIntersection<boolean>>();

// @dts-jest:pass:snap
testType<UnionToIntersection<true | false>>();

// @dts-jest:pass:snap
testType<UnionToIntersection<'name' | 'age'>>();
}
15 changes: 15 additions & 0 deletions src/mapped-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -615,3 +615,18 @@ export type AugmentedRequired<
T extends object,
K extends keyof T = keyof T
> = Omit<T, K> & Required<Pick<T, K>>;

/**
* UnionToIntersection
* @desc Get intersection type given union type `U`
* Credit: jcalz
* @see https://stackoverflow.com/a/50375286/7381355
* @example
* // Expect: { name: string } & { age: number } & { visible: boolean }
* UnionToIntersection<{ name: string } | { age: number } | { visible: boolean }>
*/
export type UnionToIntersection<U> = (U extends any
? (k: U) => void
: never) extends (k: infer I) => void
? I
: never;

0 comments on commit 5b7cd0c

Please sign in to comment.