Skip to content

Commit 3ebb063

Browse files
authored
Merge pull request #129 from andnp/Fix-Intersections
Fix intersections
2 parents 01c3c68 + 9569d53 commit 3ebb063

File tree

5 files changed

+117
-6
lines changed

5 files changed

+117
-6
lines changed

README.md

+52-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ npm install --save-dev simplytyped
1717

1818
**[Objects](#objects)**
1919

20-
[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)
20+
[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)
2121

2222
**[Utils](#utils)**
2323

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

9393
type got = CombineObjects<a, b>;
9494
type expected = {
9595
x: number,
96-
y: 'hi' & 'there',
96+
y: 'hi',
9797
z: number
9898
};
9999

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

234+
### ElementwiseIntersect
235+
Takes two objects and returns their element-wise intersection.
236+
*Note*: this removes any key-level information, such as optional or readonly keys.
237+
```ts
238+
test('Can combine two objects elementwise', t => {
239+
type a = { x: number, y: 'hi' };
240+
type b = { z: number, y: 'there' };
241+
242+
type got = ElementwiseIntersect<a, b>;
243+
type expected = {
244+
x: number,
245+
y: 'hi' & 'there',
246+
z: number,
247+
};
248+
249+
assert<got, expected>(t);
250+
assert<expected, got>(t);
251+
});
252+
253+
test('Can combine two objects with private members elementwise', t => {
254+
class A {
255+
a: number = 1;
256+
private x: number = 2;
257+
y: 'hi' = 'hi';
258+
private z: 'hey' = 'hey';
259+
}
260+
261+
class B {
262+
a: 22 = 22;
263+
private x: number = 2;
264+
y: 'there' = 'there';
265+
private z: 'friend' = 'friend';
266+
}
267+
268+
type got = ElementwiseIntersect<A, B>;
269+
type expected = {
270+
a: 22,
271+
y: 'hi' & 'there',
272+
};
273+
274+
assert<got, expected>(t);
275+
assert<expected, got>(t);
276+
});
277+
```
278+
234279
### GetKey
235280
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.
236281
Returns `never` if the key is not on the object.
@@ -471,6 +516,10 @@ For discriminated unions of objects, it is important to have a single "tag" prop
471516
Creates an object with each entry being tagged by the key defining that entry.
472517

473518

519+
### TryKey
520+
Like `GetKey`, but returns `unknown` if the key is not present on the object.
521+
522+
474523
### UnionizeProperties
475524
Get a union of the properties of an object.
476525
```ts

scripts/testTsVersions.sh

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
set -e
22

3-
for v in 3.0.3 3.1.6 3.2.2 next; do
3+
# highest patch version of each minor version
4+
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
45
npm install --no-save typescript@$v
56
npm test
67
done

src/types/objects.ts

+17
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,23 @@ export type CombineObjects<T extends object, U extends object> = ObjectType<T &
3434
* @returns `T[K]` if the key exists, `never` otherwise
3535
*/
3636
export type GetKey<T, K extends keyof any> = K extends keyof T ? T[K] : never;
37+
/**
38+
* Like `GetKey`, but returns `unknown` if the key is not present on the object.
39+
* @param T Object to get values from
40+
* @param K Key to query object for value
41+
* @returns `T[K]` if the key exists, `unknown` otherwise
42+
*/
43+
export type TryKey<T, K extends keyof any> = K extends keyof T ? T[K]: unknown;
44+
/**
45+
* Takes two objects and returns their element-wise intersection.
46+
* *Note*: this removes any key-level information, such as optional or readonly keys.
47+
* @param T First object to be intersected
48+
* @param U Second object to be intersected
49+
* @returns element-wise `T` & `U` cleaned up to look like flat object to VSCode
50+
*/
51+
export type ElementwiseIntersect<T extends object, U extends object> = {
52+
[k in (keyof T | keyof U)]: TryKey<T, k> & TryKey<U, k>;
53+
};
3754

3855
// ----
3956
// Keys

test/objects/CombineObjects.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,12 @@ import { CombineObjects } from '../../src';
55

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

1010
type got = CombineObjects<a, b>;
1111
type expected = {
1212
x: number,
13-
y: 'hi' & 'there',
13+
y: 'hi',
1414
z: number
1515
};
1616

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import test from 'ava';
2+
import { assert } from '../helpers/assert';
3+
4+
import { ElementwiseIntersect } from '../../src';
5+
6+
test('Can combine two objects elementwise', t => {
7+
type a = { x: number, y: 'hi' };
8+
type b = { z: number, y: 'there' };
9+
10+
type got = ElementwiseIntersect<a, b>;
11+
type expected = {
12+
x: number,
13+
y: 'hi' & 'there',
14+
z: number,
15+
};
16+
17+
assert<got, expected>(t);
18+
assert<expected, got>(t);
19+
});
20+
21+
test('Can combine two objects with private members elementwise', t => {
22+
class A {
23+
a: number = 1;
24+
private x: number = 2;
25+
y: 'hi' = 'hi';
26+
private z: 'hey' = 'hey';
27+
}
28+
29+
class B {
30+
a: 22 = 22;
31+
private x: number = 2;
32+
y: 'there' = 'there';
33+
private z: 'friend' = 'friend';
34+
}
35+
36+
type got = ElementwiseIntersect<A, B>;
37+
type expected = {
38+
a: 22,
39+
y: 'hi' & 'there',
40+
};
41+
42+
assert<got, expected>(t);
43+
assert<expected, got>(t);
44+
});

0 commit comments

Comments
 (0)