Skip to content

Commit 3e6727a

Browse files
committed
feat: add StrictUnion
1 parent 9810171 commit 3e6727a

File tree

2 files changed

+42
-0
lines changed

2 files changed

+42
-0
lines changed

src/types/objects.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,3 +243,23 @@ export interface ConstructorFor<T extends object> {
243243
new (...args: any[]): T;
244244
prototype: T;
245245
}
246+
247+
// ------
248+
// Unions
249+
// ------
250+
251+
/**
252+
* Makes a union 'strict', such that members are disallowed from including the keys of other members
253+
* For example, `{x: 1, y: 1}` is a valid member of `{x: number} | {y: number}`,
254+
* but it's not a valid member of StrictUnion<{x: number} | {y: number}>.
255+
* @param T a union type
256+
* @returns a the strict version of `T`
257+
*/
258+
export type StrictUnion<T> = _StrictUnionHelper<T, T>;
259+
260+
// UnionMember is actually passed as the whole union, but it's used in a distributive conditional
261+
// to refer to each individual member of the union
262+
export type _StrictUnionHelper<UnionMember, Union> =
263+
UnionMember extends any ?
264+
UnionMember & Partial<Record<Exclude<UnionKeys<Union>, keyof UnionMember>, never>>
265+
: never;

test/objects/StrictUnion.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import test from 'ava';
2+
import { assert } from '../helpers/assert';
3+
4+
import { StrictUnion } from '../../src';
5+
6+
test('disallow union members with mixed properties', t => {
7+
type a = { a: number };
8+
type b = { b: string };
9+
10+
type good1 = {a: 1};
11+
type good2 = {b: "b"};
12+
type bad = {a: 1, b: "foo"};
13+
14+
type isStrict<T> = T extends Array<StrictUnion<a | b>> ? 'Yes' : 'No';
15+
16+
type strictUnion = [good1, good2];
17+
type nonStrictUnion = [good1, good2, bad];
18+
19+
assert<isStrict<strictUnion>, 'Yes'>(t);
20+
assert<isStrict<nonStrictUnion>, 'No'>(t);
21+
22+
});

0 commit comments

Comments
 (0)