diff --git a/README.md b/README.md
index 7ecc8b4..75c8a09 100644
--- a/README.md
+++ b/README.md
@@ -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)**
@@ -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;
type expected = {
x: number,
- y: 'hi' & 'there',
+ y: 'hi',
z: number
};
@@ -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;
+ type expected = {
+ x: number,
+ y: 'hi' & 'there',
+ z: number,
+ };
+
+ assert(t);
+ assert(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;
+ type expected = {
+ a: 22,
+ y: 'hi' & 'there',
+ };
+
+ assert(t);
+ assert(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.
@@ -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
diff --git a/scripts/testTsVersions.sh b/scripts/testTsVersions.sh
index d6c1bac..319d0ce 100644
--- a/scripts/testTsVersions.sh
+++ b/scripts/testTsVersions.sh
@@ -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
diff --git a/src/types/objects.ts b/src/types/objects.ts
index 1c9f64d..36a0871 100644
--- a/src/types/objects.ts
+++ b/src/types/objects.ts
@@ -34,6 +34,23 @@ export type CombineObjects = ObjectType = 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 = 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 = {
+ [k in (keyof T | keyof U)]: TryKey & TryKey;
+};
// ----
// Keys
diff --git a/test/objects/CombineObjects.test.ts b/test/objects/CombineObjects.test.ts
index 50dac54..f7da339 100644
--- a/test/objects/CombineObjects.test.ts
+++ b/test/objects/CombineObjects.test.ts
@@ -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;
type expected = {
x: number,
- y: 'hi' & 'there',
+ y: 'hi',
z: number
};
diff --git a/test/objects/ElementwiseIntersect.test.ts b/test/objects/ElementwiseIntersect.test.ts
new file mode 100644
index 0000000..937824e
--- /dev/null
+++ b/test/objects/ElementwiseIntersect.test.ts
@@ -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;
+ type expected = {
+ x: number,
+ y: 'hi' & 'there',
+ z: number,
+ };
+
+ assert(t);
+ assert(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;
+ type expected = {
+ a: 22,
+ y: 'hi' & 'there',
+ };
+
+ assert(t);
+ assert(t);
+});