Skip to content

Commit

Permalink
Merge pull request #129 from andnp/Fix-Intersections
Browse files Browse the repository at this point in the history
Fix intersections
  • Loading branch information
andnp authored Apr 30, 2020
2 parents 01c3c68 + 9569d53 commit 3ebb063
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 6 deletions.
55 changes: 52 additions & 3 deletions 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) - [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)
[AllKeys](#allkeys) - [AllRequired](#allrequired) - [CombineObjects](#combineobjects) - [DeepPartial](#deeppartial) - [DeepReadonly](#deepreadonly) - [DiffKeys](#diffkeys) - [ElementwiseIntersect](#elementwiseintersect) - [GetKey](#getkey) - [HasKey](#haskey) - [Intersect](#intersect) - [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) - [TryKey](#trykey) - [UnionizeProperties](#unionizeproperties) - [UnionKeys](#unionkeys)

**[Utils](#utils)**

Expand Down Expand Up @@ -88,12 +88,12 @@ Useful for making extremely complex types look nice in VSCode.
```ts
test('Can combine two objects (without pesky & in vscode)', t => {
type a = { x: number, y: 'hi' };
type b = { z: number, y: 'there' };
type b = { z: number };

type got = CombineObjects<a, b>;
type expected = {
x: number,
y: 'hi' & 'there',
y: 'hi',
z: number
};

Expand Down Expand Up @@ -231,6 +231,51 @@ test('Can get all keys that are different between objects', t => {
});
```

### ElementwiseIntersect
Takes two objects and returns their element-wise intersection.
*Note*: this removes any key-level information, such as optional or readonly keys.
```ts
test('Can combine two objects elementwise', t => {
type a = { x: number, y: 'hi' };
type b = { z: number, y: 'there' };

type got = ElementwiseIntersect<a, b>;
type expected = {
x: number,
y: 'hi' & 'there',
z: number,
};

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

test('Can combine two objects with private members elementwise', t => {
class A {
a: number = 1;
private x: number = 2;
y: 'hi' = 'hi';
private z: 'hey' = 'hey';
}

class B {
a: 22 = 22;
private x: number = 2;
y: 'there' = 'there';
private z: 'friend' = 'friend';
}

type got = ElementwiseIntersect<A, B>;
type expected = {
a: 22,
y: 'hi' & 'there',
};

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

### GetKey
Gets the value of specified property on any object without compile time error (`Property 'b' does not exist on type '{ a: string; }'.`) and the like.
Returns `never` if the key is not on the object.
Expand Down Expand Up @@ -471,6 +516,10 @@ For discriminated unions of objects, it is important to have a single "tag" prop
Creates an object with each entry being tagged by the key defining that entry.


### TryKey
Like `GetKey`, but returns `unknown` if the key is not present on the object.


### UnionizeProperties
Get a union of the properties of an object.
```ts
Expand Down
3 changes: 2 additions & 1 deletion scripts/testTsVersions.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
set -e

for v in 3.0.3 3.1.6 3.2.2 next; do
# highest patch version of each minor version
for v in 3.0.3 3.1.6 3.2.4 3.3.4000 3.4.5 3.5.3 3.6.5 3.7.5 3.8.3 next; do
npm install --no-save typescript@$v
npm test
done
17 changes: 17 additions & 0 deletions src/types/objects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,23 @@ export type CombineObjects<T extends object, U extends object> = ObjectType<T &
* @returns `T[K]` if the key exists, `never` otherwise
*/
export type GetKey<T, K extends keyof any> = K extends keyof T ? T[K] : never;
/**
* Like `GetKey`, but returns `unknown` if the key is not present on the object.
* @param T Object to get values from
* @param K Key to query object for value
* @returns `T[K]` if the key exists, `unknown` otherwise
*/
export type TryKey<T, K extends keyof any> = K extends keyof T ? T[K]: unknown;
/**
* Takes two objects and returns their element-wise intersection.
* *Note*: this removes any key-level information, such as optional or readonly keys.
* @param T First object to be intersected
* @param U Second object to be intersected
* @returns element-wise `T` & `U` cleaned up to look like flat object to VSCode
*/
export type ElementwiseIntersect<T extends object, U extends object> = {
[k in (keyof T | keyof U)]: TryKey<T, k> & TryKey<U, k>;
};

// ----
// Keys
Expand Down
4 changes: 2 additions & 2 deletions test/objects/CombineObjects.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ import { CombineObjects } from '../../src';

test('Can combine two objects (without pesky & in vscode)', t => {
type a = { x: number, y: 'hi' };
type b = { z: number, y: 'there' };
type b = { z: number };

type got = CombineObjects<a, b>;
type expected = {
x: number,
y: 'hi' & 'there',
y: 'hi',
z: number
};

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

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

test('Can combine two objects elementwise', t => {
type a = { x: number, y: 'hi' };
type b = { z: number, y: 'there' };

type got = ElementwiseIntersect<a, b>;
type expected = {
x: number,
y: 'hi' & 'there',
z: number,
};

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

test('Can combine two objects with private members elementwise', t => {
class A {
a: number = 1;
private x: number = 2;
y: 'hi' = 'hi';
private z: 'hey' = 'hey';
}

class B {
a: 22 = 22;
private x: number = 2;
y: 'there' = 'there';
private z: 'friend' = 'friend';
}

type got = ElementwiseIntersect<A, B>;
type expected = {
a: 22,
y: 'hi' & 'there',
};

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

0 comments on commit 3ebb063

Please sign in to comment.