Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});