Skip to content

Commit

Permalink
Merge pull request #77 from andnp/StrictUnion
Browse files Browse the repository at this point in the history
Add StrictUnion
  • Loading branch information
andnp authored Jan 9, 2019
2 parents 96320a2 + ccf97e0 commit 4d46243
Show file tree
Hide file tree
Showing 4 changed files with 111 additions and 1 deletion.
44 changes: 43 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ npm install --save-dev simplytyped

**[Objects](#objects)**

[AllKeys](#allkeys) - [AllRequired](#allrequired) - [CombineObjects](#combineobjects) - [DeepPartial](#deeppartial) - [DeepReadonly](#deepreadonly) - [DiffKeys](#diffkeys) - [GetKey](#getkey) - [HasKey](#haskey) - [Intersect](#intersect) - [Keys](#keys) - [KeysByType](#keysbytype) - [Merge](#merge) - [ObjectKeys](#objectkeys) - [ObjectType](#objecttype) - [Omit](#omit) - [Optional](#optional) - [Overwrite](#overwrite) - [PlainObject](#plainobject) - [PureKeys](#purekeys) - [Required](#required) - [SharedKeys](#sharedkeys) - [StringKeys](#stringkeys) - [TaggedObject](#taggedobject) - [UnionizeProperties](#unionizeproperties)
[AllKeys](#allkeys) - [AllRequired](#allrequired) - [CombineObjects](#combineobjects) - [DeepPartial](#deeppartial) - [DeepReadonly](#deepreadonly) - [DiffKeys](#diffkeys) - [GetKey](#getkey) - [HasKey](#haskey) - [Intersect](#intersect) - [Keys](#keys) - [KeysByType](#keysbytype) - [Merge](#merge) - [ObjectKeys](#objectkeys) - [ObjectType](#objecttype) - [Omit](#omit) - [Optional](#optional) - [Overwrite](#overwrite) - [PlainObject](#plainobject) - [PureKeys](#purekeys) - [Required](#required) - [SharedKeys](#sharedkeys) - [StrictUnion](#strictunion) - [StringKeys](#stringkeys) - [TaggedObject](#taggedobject) - [UnionizeProperties](#unionizeproperties) - [UnionKeys](#unionkeys)

**[Utils](#utils)**

Expand Down Expand Up @@ -466,6 +466,31 @@ test('Can get keys that are same between objects', t => {

```

### StrictUnion
Makes a union 'strict', such that members are disallowed from including the keys of other members
For example, `{x: 1, y: 1}` is a valid member of `{x: number} | {y: number}`,
but it's not a valid member of StrictUnion<{x: number} | {y: number}>.
```ts
test('disallow union members with mixed properties', t => {
type a = { a: number };
type b = { b: string };

type good1 = {a: 1};
type good2 = {b: "b"};
type bad = {a: 1, b: "foo"};

type isStrict<T> = T extends Array<StrictUnion<a | b>> ? 'Yes' : 'No';

type strictUnion = [good1, good2];
type nonStrictUnion = [good1, good2, bad];

assert<isStrict<strictUnion>, 'Yes'>(t);
assert<isStrict<nonStrictUnion>, 'No'>(t);

});

```

### StringKeys
Typescript 2.9 introduced `number | symbol` as possible results from `keyof any`.
For backwards compatibility with objects containing only `string` keys, this will
Expand All @@ -492,6 +517,23 @@ test('Can get a union of all values in an object', t => {

```

### UnionKeys

```ts
test('Can get all keys between objects in a union', t => {
type a = { w: number, x: string };
type b = { x: number, z: boolean };
type c = { y: boolean, z: string };

type got = UnionKeys<a | b | c>;
type expected = 'w' | 'x' | 'y' | 'z';

assert<got, expected>(t);
assert<expected, got>(t);
});

```

## Utils

### NoInfer
Expand Down
30 changes: 30 additions & 0 deletions src/types/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,15 @@ export type DiffKeys<T, U> = Diff<Keys<T>, Keys<U>>;
*/
export type HasKey<T, K extends keyof any> = K extends Keys<T> ? True : False;

/**
* @param T the union to get the keys of
* @returns a union containing all the keys of members of `T`
*/
export type UnionKeys<T>
// Using a conditional here, so that it distributes over members of the union
// See https://www.typescriptlang.org/docs/handbook/advanced-types.html#distributive-conditional-types
= T extends any ? keyof T : never;

// -------------
// Manipulations
// -------------
Expand Down Expand Up @@ -234,3 +243,24 @@ export interface ConstructorFor<T extends object> {
new (...args: any[]): T;
prototype: T;
}

// ------
// Unions
// ------

/**
* Makes a union 'strict', such that members are disallowed from including the keys of other members
* For example, `{x: 1, y: 1}` is a valid member of `{x: number} | {y: number}`,
* but it's not a valid member of StrictUnion<{x: number} | {y: number}>.
* @param T a union type
* @returns a the strict version of `T`
*/
export type StrictUnion<T> = _StrictUnionHelper<T, T>;

// UnionMember is actually passed as the whole union, but it's used in a distributive conditional
// to refer to each individual member of the union
/** no-doc */
export type _StrictUnionHelper<UnionMember, Union> =
UnionMember extends any ?
UnionMember & Partial<Record<Exclude<UnionKeys<Union>, keyof UnionMember>, never>>
: never;
22 changes: 22 additions & 0 deletions test/objects/StrictUnion.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import test from 'ava';
import { assert } from '../helpers/assert';

import { StrictUnion } from '../../src';

test('disallow union members with mixed properties', t => {
type a = { a: number };
type b = { b: string };

type good1 = {a: 1};
type good2 = {b: "b"};
type bad = {a: 1, b: "foo"};

type isStrict<T> = T extends Array<StrictUnion<a | b>> ? 'Yes' : 'No';

type strictUnion = [good1, good2];
type nonStrictUnion = [good1, good2, bad];

assert<isStrict<strictUnion>, 'Yes'>(t);
assert<isStrict<nonStrictUnion>, 'No'>(t);

});
16 changes: 16 additions & 0 deletions test/objects/UnionKeys.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import test from 'ava';
import { assert } from '../helpers/assert';

import { AllKeys, UnionKeys } from '../../src';

test('Can get all keys between objects in a union', t => {
type a = { w: number, x: string };
type b = { x: number, z: boolean };
type c = { y: boolean, z: string };

type got = UnionKeys<a | b | c>;
type expected = 'w' | 'x' | 'y' | 'z';

assert<got, expected>(t);
assert<expected, got>(t);
});

0 comments on commit 4d46243

Please sign in to comment.