From af8854b19815419639c497835b0f2d1531b4e2aa Mon Sep 17 00:00:00 2001 From: harris-miller Date: Tue, 18 Apr 2023 22:48:37 -0600 Subject: [PATCH 01/20] updates --- types/assoc.d.ts | 77 ++++++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 32 deletions(-) diff --git a/types/assoc.d.ts b/types/assoc.d.ts index 1814c87e..f84c2e2e 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -1,51 +1,69 @@ import { Placeholder } from './util/tools'; -// assoc(__, val, obj)(prop), this tests if prop is keyof obj and if val is typeof obj[prop] for best return type -export function assoc(__: Placeholder, val: T, obj: U): (prop: K) => K extends keyof U ? T extends U[K] ? U : Record & Omit : U & Record; -// assoc(prop, __, obj)(val), when K is keyof obj, tests if val is typeof obj[prop] for best return type -export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => T extends U[K] ? U : Record & Omit; -// assoc(prop, __, obj)(val), when prop is not keyof obj -export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => U & Record; // assoc(prop, val, obj) when prop is keyof obj and val is same type export function assoc(prop: K, val: U[K], obj: U): U; // assoc(prop, val, obj) when prop is keyof obj and val is not same type -export function assoc(prop: K, val: T, obj: U): Record & Omit; +export function assoc(prop: K, val: T extends Placeholder ? never : T, obj: U): Omit & Record; // assoc(prop, val, obj) when prop is not keyof obj -export function assoc(prop: K, val: T, obj: U): U & Record; - +export function assoc(prop: K extends Placeholder ? never : K, val: T extends Placeholder ? never : T, obj: U): U & Record; +// assoc(__, val, obj)(prop), this tests if prop is keyof obj and if val is typeof obj[prop] for best return type +export function assoc(__: Placeholder, val: T extends Placeholder ? never : T, obj: U): (prop: K) => K extends keyof U ? T extends U[K] ? U : Omit & Record : U & Record; +// assoc(prop, __, obj)(val), when K is keyof obj, tests if val is typeof obj[prop] for best return type +export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => T extends U[K] ? U : Omit & Record; +// assoc(prop, __, obj)(val), when prop is not keyof obj +export function assoc(prop: K extends Placeholder ? never : K, __: Placeholder, obj: U): (val: T) => U & Record; +// assoc (__, __, obj) +export function assoc(__: Placeholder, __2: Placeholder, obj: U): { + // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is same type + (prop: K, val: U[K]): U; + // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is not same type + (prop: K, val: T extends Placeholder ? never : T): Omit & Record; + // assoc(__, __, obj)(prop, val) when prop is not keyof obj + (prop: K extends Placeholder ? never : K, val: T extends Placeholder ? never : T): U & Record; + // assoc(__, __, obj)(__, val)(prop) + (__: Placeholder, val: T extends Placeholder ? never : T): (prop: K) => T extends U[K] ? U : Omit & Record; + // assoc(__, __, obj)(prop)(val) prop is keyof obj + (prop: K): (val: T extends Placeholder ? never : T) => T extends U[K] ? U : Omit & Record; + // assoc(__, __, obj)(prop)(val) prop is not keyof obj + (prop: K extends Placeholder ? never : K): (val: T extends Placeholder ? never : T) => U & Record; +}; +// assoc(prop, val) -- TODO, look into `map` problem +export function assoc(prop: K extends Placeholder ? never : K, val: T): { + // assoc(prop, val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type + >(obj: U): T extends U[K] ? U : Omit & Record; + // assoc(prop, val)(obj), when obj does not have key prop + (obj: U): U & Record; +}; // assoc(__, val) export function assoc(__: Placeholder, val: T) : { + // assoc(__, val)(prop, obj), when obj has key prop, tests if val is typeof obj[prop] for best return type + (prop: K, obj: U): U[K] extends T ? U : Omit & Record; + // assoc(__, val)(prop, obj), when obj does not have key prop + (prop: K, obj: U): K extends Placeholder ? never : U & Record; + // assoc(__, val)(__, obj) (__2: Placeholder, obj: U): { // assoc(__, val)(__, obj)(prop), prop is keyof obj, tests if val is typeof obj[prop] for best return type - (prop: K): U[K] extends T ? U : Record & Omit; + (prop: K): U[K] extends T ? U : Omit & Record; // assoc(__, val)(__, obj)(prop), prop is not keyof obj - (prop: K): U & Record; + (prop: K): U & Record; }; - // assoc(__, val)(prop, obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - (prop: K, obj: U): U[K] extends T ? U : Record & Omit; - // assoc(__, val)(prop, obj), when obj does not have key prop - (prop: K, obj: U): U & Record; // assoc(__, val)(prop) - (prop: K): { + (prop: K extends Placeholder ? never : K): { // assoc(__, val)(prop)(obj) when obj has key prop, tests if val is typeof obj[prop] for best return type - >(obj: U): U[K] extends T ? U : Record & Omit; + >(obj: U): U[K] extends T ? U : Omit & Record; // assoc(__, val)(prop)(obj) when obj does not have key prop (obj: U): U & Record; } }; - -// assoc(prop, val) -export function assoc(prop: K, val: T) : { - // assoc(prop, val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - >(obj: U): U[K] extends T ? U : Record & Omit; - // assoc(prop, val)(obj), when obj does not have key prop - (obj: U): U & Record; -}; - // assoc(prop) -export function assoc(prop: K): { +export function assoc(prop: K): { + // assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type + >(val: T extends Placeholder ? never : T, obj: U): U[K] extends T ? U : Record & Omit; + // assoc(prop)(val, obj) when obj does not have a key prop + (val: T, obj: U): U & Record; + // assoc(prop)(__, obj) when prop is keyof obj >(__: Placeholder, obj: U): { // assoc(prop)(__, obj)(val) if val is typeof obj[prop] @@ -56,11 +74,6 @@ export function assoc(prop: K): { // assoc(prop)(__, obj) when prop is not keyof obj (__: Placeholder, obj: U): (val: T) => U & Record; - // assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type - >(val: T, obj: U): U[K] extends T ? U : Record & Omit; - // assoc(prop)(val, obj) when obj does not have a key prop - (val: T, obj: U): U & Record; - // assoc(prop)(val) (val: T): { // assoc(prop)(val)(obj) when obj has key prop and val is typeof obj[prop] From 072d19c59ac4fc1d90680ea872f1dd7b0da5b7ab Mon Sep 17 00:00:00 2001 From: harris-miller Date: Tue, 18 Apr 2023 23:34:55 -0600 Subject: [PATCH 02/20] removed unused generic --- types/assoc.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/assoc.d.ts b/types/assoc.d.ts index f84c2e2e..e6f58dbd 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -13,7 +13,7 @@ export function assoc(prop: K, __: Placeholder, obj: U): < // assoc(prop, __, obj)(val), when prop is not keyof obj export function assoc(prop: K extends Placeholder ? never : K, __: Placeholder, obj: U): (val: T) => U & Record; // assoc (__, __, obj) -export function assoc(__: Placeholder, __2: Placeholder, obj: U): { +export function assoc(__: Placeholder, __2: Placeholder, obj: U): { // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is same type (prop: K, val: U[K]): U; // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is not same type From 3ee454a5612db8a956edddfe383b9e6ec100d1c6 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Wed, 24 May 2023 23:38:20 -0600 Subject: [PATCH 03/20] re-order, need to verify --- types/assoc.d.ts | 139 ++++++++++++++++++++++++----------------------- 1 file changed, 71 insertions(+), 68 deletions(-) diff --git a/types/assoc.d.ts b/types/assoc.d.ts index e6f58dbd..4e4cac09 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -1,68 +1,16 @@ import { Placeholder } from './util/tools'; -// assoc(prop, val, obj) when prop is keyof obj and val is same type -export function assoc(prop: K, val: U[K], obj: U): U; -// assoc(prop, val, obj) when prop is keyof obj and val is not same type -export function assoc(prop: K, val: T extends Placeholder ? never : T, obj: U): Omit & Record; -// assoc(prop, val, obj) when prop is not keyof obj -export function assoc(prop: K extends Placeholder ? never : K, val: T extends Placeholder ? never : T, obj: U): U & Record; -// assoc(__, val, obj)(prop), this tests if prop is keyof obj and if val is typeof obj[prop] for best return type -export function assoc(__: Placeholder, val: T extends Placeholder ? never : T, obj: U): (prop: K) => K extends keyof U ? T extends U[K] ? U : Omit & Record : U & Record; -// assoc(prop, __, obj)(val), when K is keyof obj, tests if val is typeof obj[prop] for best return type -export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => T extends U[K] ? U : Omit & Record; -// assoc(prop, __, obj)(val), when prop is not keyof obj -export function assoc(prop: K extends Placeholder ? never : K, __: Placeholder, obj: U): (val: T) => U & Record; -// assoc (__, __, obj) -export function assoc(__: Placeholder, __2: Placeholder, obj: U): { - // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is same type - (prop: K, val: U[K]): U; - // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is not same type - (prop: K, val: T extends Placeholder ? never : T): Omit & Record; - // assoc(__, __, obj)(prop, val) when prop is not keyof obj - (prop: K extends Placeholder ? never : K, val: T extends Placeholder ? never : T): U & Record; - // assoc(__, __, obj)(__, val)(prop) - (__: Placeholder, val: T extends Placeholder ? never : T): (prop: K) => T extends U[K] ? U : Omit & Record; - // assoc(__, __, obj)(prop)(val) prop is keyof obj - (prop: K): (val: T extends Placeholder ? never : T) => T extends U[K] ? U : Omit & Record; - // assoc(__, __, obj)(prop)(val) prop is not keyof obj - (prop: K extends Placeholder ? never : K): (val: T extends Placeholder ? never : T) => U & Record; -}; -// assoc(prop, val) -- TODO, look into `map` problem -export function assoc(prop: K extends Placeholder ? never : K, val: T): { - // assoc(prop, val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - >(obj: U): T extends U[K] ? U : Omit & Record; - // assoc(prop, val)(obj), when obj does not have key prop - (obj: U): U & Record; -}; -// assoc(__, val) -export function assoc(__: Placeholder, val: T) : { - // assoc(__, val)(prop, obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - (prop: K, obj: U): U[K] extends T ? U : Omit & Record; - // assoc(__, val)(prop, obj), when obj does not have key prop - (prop: K, obj: U): K extends Placeholder ? never : U & Record; - - // assoc(__, val)(__, obj) - (__2: Placeholder, obj: U): { - // assoc(__, val)(__, obj)(prop), prop is keyof obj, tests if val is typeof obj[prop] for best return type - (prop: K): U[K] extends T ? U : Omit & Record; - // assoc(__, val)(__, obj)(prop), prop is not keyof obj - (prop: K): U & Record; - }; - - // assoc(__, val)(prop) - (prop: K extends Placeholder ? never : K): { - // assoc(__, val)(prop)(obj) when obj has key prop, tests if val is typeof obj[prop] for best return type - >(obj: U): U[K] extends T ? U : Omit & Record; - // assoc(__, val)(prop)(obj) when obj does not have key prop - (obj: U): U & Record; - } -}; // assoc(prop) export function assoc(prop: K): { - // assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type - >(val: T extends Placeholder ? never : T, obj: U): U[K] extends T ? U : Record & Omit; - // assoc(prop)(val, obj) when obj does not have a key prop - (val: T, obj: U): U & Record; + // assoc(prop)(val) + (val: T): { + // assoc(prop)(val)(obj) when obj has key prop and val is typeof obj[prop] + >(obj: U): U; + // assoc(prop)(val)(obj) when obj has key prop and val is not typeof obj[prop] + >(obj: U): Record & Omit; + // assoc(prop)(val)(obj) when obj does not have key prop + (obj: U): U & Record; + } // assoc(prop)(__, obj) when prop is keyof obj >(__: Placeholder, obj: U): { @@ -71,16 +19,71 @@ export function assoc(prop: K): { // assoc(prop)(__, obj)(val) if val is not typeof obj[prop] (val: T): Record & Omit; } + // assoc(prop)(__, obj) when prop is not keyof obj (__: Placeholder, obj: U): (val: T) => U & Record; - // assoc(prop)(val) - (val: T): { - // assoc(prop)(val)(obj) when obj has key prop and val is typeof obj[prop] - >(obj: U): U; - // assoc(prop)(val)(obj) when obj has key prop and val is not typeof obj[prop] - >(obj: U): Record & Omit; - // assoc(prop)(val)(obj) when obj does not have key prop + // assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type + >(val: T extends Placeholder ? never : T, obj: U): U[K] extends T ? U : Record & Omit; + // assoc(prop)(val, obj) when obj does not have a key prop + (val: T, obj: U): U & Record; +}; + +// assoc(__, val) +export function assoc(__: Placeholder, val: T) : { + // assoc(__, val)(prop) + (prop: K extends Placeholder ? never : K): { + // assoc(__, val)(prop)(obj) when obj has key prop, tests if val is typeof obj[prop] for best return type + >(obj: U): U[K] extends T ? U : Omit & Record; + // assoc(__, val)(prop)(obj) when obj does not have key prop (obj: U): U & Record; } + + // assoc(__, val)(__, obj) + (__2: Placeholder, obj: U): { + // assoc(__, val)(__, obj)(prop), prop is keyof obj, tests if val is typeof obj[prop] for best return type + (prop: K): U[K] extends T ? U : Omit & Record; + // assoc(__, val)(__, obj)(prop), prop is not keyof obj + (prop: K): U & Record; + }; + + // assoc(__, val)(prop, obj), when obj has key prop, tests if val is typeof obj[prop] for best return type + (prop: K, obj: U): U[K] extends T ? U : Omit & Record; + // assoc(__, val)(prop, obj), when obj does not have key prop + (prop: K, obj: U): K extends Placeholder ? never : U & Record; }; +// assoc(prop, val) +export function assoc(prop: K extends Placeholder ? never : K, val: T): { + // assoc(prop, val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type + >(obj: U): T extends U[K] ? U : Omit & Record; + // assoc(prop, val)(obj), when obj does not have key prop + (obj: U): U & Record; +}; + +// assoc (__, __, obj) +export function assoc(__: Placeholder, __2: Placeholder, obj: U): { + // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is same type + (prop: K, val: U[K]): U; + // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is not same type + (prop: K, val: T extends Placeholder ? never : T): Omit & Record; + // assoc(__, __, obj)(prop, val) when prop is not keyof obj + (prop: K extends Placeholder ? never : K, val: T extends Placeholder ? never : T): U & Record; + // assoc(__, __, obj)(__, val)(prop) + (__: Placeholder, val: T extends Placeholder ? never : T): (prop: K) => T extends U[K] ? U : Omit & Record; + // assoc(__, __, obj)(prop)(val) prop is keyof obj + (prop: K): (val: T extends Placeholder ? never : T) => T extends U[K] ? U : Omit & Record; + // assoc(__, __, obj)(prop)(val) prop is not keyof obj + (prop: K extends Placeholder ? never : K): (val: T extends Placeholder ? never : T) => U & Record; +}; +// assoc(prop, __, obj)(val), when prop is not keyof obj +export function assoc(prop: K extends Placeholder ? never : K, __: Placeholder, obj: U): (val: T) => U & Record; +// assoc(prop, __, obj)(val), when K is keyof obj, tests if val is typeof obj[prop] for best return type +export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => T extends U[K] ? U : Omit & Record; +// assoc(__, val, obj)(prop), this tests if prop is keyof obj and if val is typeof obj[prop] for best return type +export function assoc(__: Placeholder, val: T extends Placeholder ? never : T, obj: U): (prop: K) => K extends keyof U ? T extends U[K] ? U : Omit & Record : U & Record; +// assoc(prop, val, obj) when prop is not keyof obj +export function assoc(prop: K extends Placeholder ? never : K, val: T extends Placeholder ? never : T, obj: U): U & Record; +// assoc(prop, val, obj) when prop is keyof obj and val is not same type +export function assoc(prop: K, val: T extends Placeholder ? never : T, obj: U): Omit & Record; +// assoc(prop, val, obj) when prop is keyof obj and val is same type +export function assoc(prop: K, val: U[K], obj: U): U; From 658effae15dc53880c462851c193c3d68bf1d594 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Thu, 25 May 2023 00:20:22 -0600 Subject: [PATCH 04/20] need to doubel test instead of overload --- types/assoc.d.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/types/assoc.d.ts b/types/assoc.d.ts index 4e4cac09..08e92ddb 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -55,9 +55,7 @@ export function assoc(__: Placeholder, val: T) : { // assoc(prop, val) export function assoc(prop: K extends Placeholder ? never : K, val: T): { // assoc(prop, val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - >(obj: U): T extends U[K] ? U : Omit & Record; - // assoc(prop, val)(obj), when obj does not have key prop - (obj: U): U & Record; + (obj: U): U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; }; // assoc (__, __, obj) From 001451c976e7f7be363fb61c9d53b40a57ba9efc Mon Sep 17 00:00:00 2001 From: harris-miller Date: Thu, 25 May 2023 01:29:41 -0600 Subject: [PATCH 05/20] improvements and testing --- test/assoc.test.ts | 24 ++++++++++++++++++++++++ types/assoc.d.ts | 25 +++++++------------------ 2 files changed, 31 insertions(+), 18 deletions(-) create mode 100644 test/assoc.test.ts diff --git a/test/assoc.test.ts b/test/assoc.test.ts new file mode 100644 index 00000000..cf01f1f3 --- /dev/null +++ b/test/assoc.test.ts @@ -0,0 +1,24 @@ +import { expectType } from 'tsd'; +import { __, assoc } from '../es'; + +type BasicObj = { + str: string; + num: number; +}; + +const obj: BasicObj = { str: 'foo', num: 1 }; + +// assoc(key)(__, obj)(val) +expectType(assoc('str')(__, obj)('bar')); +expectType & Record<'str', number>>(assoc('str')(__, obj)(2)); +expectType & Record<'str', string>>(assoc('str')(__, { other: 'whatever' })('foo')); + +// assoc(key)(val, obj) +expectType(assoc('str')('bar', obj)); +expectType & Record<'str', number>>(assoc('str')(2, obj)); +expectType & Record<'str', string>>(assoc('str')('foo', { other: 'whatever' })); + +// assoc(key)(val)(obj) +expectType(assoc('str')('bar')(obj)); +expectType & Record<'str', number>>(assoc('str')(2)(obj)); +expectType & Record<'str', string>>(assoc('str')('foo')({ other: 'whatever' })); diff --git a/types/assoc.d.ts b/types/assoc.d.ts index 08e92ddb..78f9199d 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -4,29 +4,18 @@ import { Placeholder } from './util/tools'; export function assoc(prop: K): { // assoc(prop)(val) (val: T): { - // assoc(prop)(val)(obj) when obj has key prop and val is typeof obj[prop] - >(obj: U): U; - // assoc(prop)(val)(obj) when obj has key prop and val is not typeof obj[prop] - >(obj: U): Record & Omit; - // assoc(prop)(val)(obj) when obj does not have key prop - (obj: U): U & Record; + // assoc(prop)(val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type + (obj: U): U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; } - // assoc(prop)(__, obj) when prop is keyof obj - >(__: Placeholder, obj: U): { - // assoc(prop)(__, obj)(val) if val is typeof obj[prop] - (val: T): U; - // assoc(prop)(__, obj)(val) if val is not typeof obj[prop] - (val: T): Record & Omit; + // assoc(prop)(__, obj) + (__: Placeholder, obj: U): { + // assoc(prop)(__, obj)(val), when obj has key prop, tests if val is typeof obj[prop] for best return type + (val: T): U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; } - // assoc(prop)(__, obj) when prop is not keyof obj - (__: Placeholder, obj: U): (val: T) => U & Record; - // assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type - >(val: T extends Placeholder ? never : T, obj: U): U[K] extends T ? U : Record & Omit; - // assoc(prop)(val, obj) when obj does not have a key prop - (val: T, obj: U): U & Record; + (val: T, obj: U): U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; }; // assoc(__, val) From 79227b8be1a234946d1644f76e3f87c9627c5e2f Mon Sep 17 00:00:00 2001 From: harris-miller Date: Thu, 25 May 2023 01:45:41 -0600 Subject: [PATCH 06/20] more improvements --- test/assoc.test.ts | 24 ++++++++++++++++++++++++ types/assoc.d.ts | 29 ++++++++++++----------------- types/util/tools.d.ts | 5 +++++ 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/test/assoc.test.ts b/test/assoc.test.ts index cf01f1f3..7c59a97e 100644 --- a/test/assoc.test.ts +++ b/test/assoc.test.ts @@ -8,6 +8,10 @@ type BasicObj = { const obj: BasicObj = { str: 'foo', num: 1 }; +// +// assoc(key) +// + // assoc(key)(__, obj)(val) expectType(assoc('str')(__, obj)('bar')); expectType & Record<'str', number>>(assoc('str')(__, obj)(2)); @@ -22,3 +26,23 @@ expectType & Record<'str', string>>(assoc('str')( expectType(assoc('str')('bar')(obj)); expectType & Record<'str', number>>(assoc('str')(2)(obj)); expectType & Record<'str', string>>(assoc('str')('foo')({ other: 'whatever' })); + + +// +// assoc(__, val) +// + +// assoc(__, val)(key)(obj) +expectType(assoc(__, 'bar')('str')(obj)); +expectType & Record<'str', number>>(assoc(__, 2)('str')(obj)); +expectType & Record<'str', string>>(assoc(__, 'bar')('str')({ other: 'whatever'})); + +// assoc(__, val)(__, key)(obj) +expectType(assoc(__, 'bar')(__, obj)('str')); +expectType & Record<'str', number>>(assoc(__, 2)(__, obj)('str')); +expectType & Record<'str', string>>(assoc(__, 'bar')(__, { other: 'whatever'})('str')); + +// assoc(__, val)(key, obj) +expectType(assoc(__, 'bar')('str', obj)); +expectType & Record<'str', number>>(assoc(__, 2)('str', obj)); +expectType & Record<'str', string>>(assoc(__, 'bar')('str', { other: 'whatever'})); diff --git a/types/assoc.d.ts b/types/assoc.d.ts index 78f9199d..fb8f8c38 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -1,50 +1,45 @@ -import { Placeholder } from './util/tools'; +import { Placeholder, AssocResults } from './util/tools'; // assoc(prop) export function assoc(prop: K): { // assoc(prop)(val) (val: T): { // assoc(prop)(val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - (obj: U): U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; + (obj: U): AssocResults; } // assoc(prop)(__, obj) (__: Placeholder, obj: U): { // assoc(prop)(__, obj)(val), when obj has key prop, tests if val is typeof obj[prop] for best return type - (val: T): U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; + (val: T): AssocResults; } // assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type - (val: T, obj: U): U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; + (val: T, obj: U): AssocResults; }; // assoc(__, val) export function assoc(__: Placeholder, val: T) : { // assoc(__, val)(prop) (prop: K extends Placeholder ? never : K): { - // assoc(__, val)(prop)(obj) when obj has key prop, tests if val is typeof obj[prop] for best return type - >(obj: U): U[K] extends T ? U : Omit & Record; - // assoc(__, val)(prop)(obj) when obj does not have key prop - (obj: U): U & Record; + // assoc(__, val)(prop)(obj) + (obj: U): AssocResults; } // assoc(__, val)(__, obj) (__2: Placeholder, obj: U): { - // assoc(__, val)(__, obj)(prop), prop is keyof obj, tests if val is typeof obj[prop] for best return type - (prop: K): U[K] extends T ? U : Omit & Record; - // assoc(__, val)(__, obj)(prop), prop is not keyof obj - (prop: K): U & Record; + // assoc(__, val)(__, obj)(prop) + (prop: K): AssocResults; }; - // assoc(__, val)(prop, obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - (prop: K, obj: U): U[K] extends T ? U : Omit & Record; - // assoc(__, val)(prop, obj), when obj does not have key prop - (prop: K, obj: U): K extends Placeholder ? never : U & Record; + // assoc(__, val)(prop, obj) + (prop: K, obj: U): AssocResults; }; + // assoc(prop, val) export function assoc(prop: K extends Placeholder ? never : K, val: T): { // assoc(prop, val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - (obj: U): U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; + (obj: U): AssocResults; }; // assoc (__, __, obj) diff --git a/types/util/tools.d.ts b/types/util/tools.d.ts index c80c6d22..9d70ad3f 100644 --- a/types/util/tools.d.ts +++ b/types/util/tools.d.ts @@ -501,6 +501,11 @@ export type WidenLiterals = ? number : T; +/** + * Alias for the common result type for the `assoc` function + */ +export type AssocResults = U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; + /** * Extract the types from an array * Works with Tuples, eg `ElementOf` => `'p1' | 'p2'` From ee3df3f973ffe82be184fc02f6c5efff6f7b9840 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Thu, 25 May 2023 01:50:30 -0600 Subject: [PATCH 07/20] more tests --- test/assoc.test.ts | 9 +++++++++ types/assoc.d.ts | 1 + 2 files changed, 10 insertions(+) diff --git a/test/assoc.test.ts b/test/assoc.test.ts index 7c59a97e..f134e66d 100644 --- a/test/assoc.test.ts +++ b/test/assoc.test.ts @@ -46,3 +46,12 @@ expectType & Record<'str', string>>(assoc(__, 'ba expectType(assoc(__, 'bar')('str', obj)); expectType & Record<'str', number>>(assoc(__, 2)('str', obj)); expectType & Record<'str', string>>(assoc(__, 'bar')('str', { other: 'whatever'})); + +// +// assoc(key, val) +// + +// assoc(key, val)(obj) +expectType(assoc('str', 'bar')(obj)); +expectType & Record<'str', number>>(assoc('str', 2)(obj)); +expectType & Record<'str', string>>(assoc('str', 'foo')({ other: 'whatever' })); diff --git a/types/assoc.d.ts b/types/assoc.d.ts index fb8f8c38..0c01d815 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -57,6 +57,7 @@ export function assoc(__: Placeholder, __2: Placeholder, obj: U): { // assoc(__, __, obj)(prop)(val) prop is not keyof obj (prop: K extends Placeholder ? never : K): (val: T extends Placeholder ? never : T) => U & Record; }; + // assoc(prop, __, obj)(val), when prop is not keyof obj export function assoc(prop: K extends Placeholder ? never : K, __: Placeholder, obj: U): (val: T) => U & Record; // assoc(prop, __, obj)(val), when K is keyof obj, tests if val is typeof obj[prop] for best return type From fb255c521ba8f7821d8a7afde49cc984b10b540a Mon Sep 17 00:00:00 2001 From: harris-miller Date: Thu, 25 May 2023 02:12:20 -0600 Subject: [PATCH 08/20] more --- test/assoc.test.ts | 30 +++++++++++++++++++++++++----- types/assoc.d.ts | 18 +++++++----------- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/test/assoc.test.ts b/test/assoc.test.ts index f134e66d..d1aa8df8 100644 --- a/test/assoc.test.ts +++ b/test/assoc.test.ts @@ -1,4 +1,4 @@ -import { expectType } from 'tsd'; +import { expectType, expectError } from 'tsd'; import { __, assoc } from '../es'; type BasicObj = { @@ -15,17 +15,17 @@ const obj: BasicObj = { str: 'foo', num: 1 }; // assoc(key)(__, obj)(val) expectType(assoc('str')(__, obj)('bar')); expectType & Record<'str', number>>(assoc('str')(__, obj)(2)); -expectType & Record<'str', string>>(assoc('str')(__, { other: 'whatever' })('foo')); +expectType & Record<'str', string>>(assoc('str')(__, {} as { other: string })('foo')); // assoc(key)(val, obj) expectType(assoc('str')('bar', obj)); expectType & Record<'str', number>>(assoc('str')(2, obj)); -expectType & Record<'str', string>>(assoc('str')('foo', { other: 'whatever' })); +expectType & Record<'str', string>>(assoc('str')('foo', {} as { other: string })); // assoc(key)(val)(obj) expectType(assoc('str')('bar')(obj)); expectType & Record<'str', number>>(assoc('str')(2)(obj)); -expectType & Record<'str', string>>(assoc('str')('foo')({ other: 'whatever' })); +expectType & Record<'str', string>>(assoc('str')('foo')({} as { other: string })); // @@ -54,4 +54,24 @@ expectType & Record<'str', string>>(assoc(__, 'ba // assoc(key, val)(obj) expectType(assoc('str', 'bar')(obj)); expectType & Record<'str', number>>(assoc('str', 2)(obj)); -expectType & Record<'str', string>>(assoc('str', 'foo')({ other: 'whatever' })); +expectType & Record<'str', string>>(assoc('str', 'foo')({} as { other: string })); + +// +// assoc__, __, obj) +// + +// assoc(__, __, obj)(key)(val) +expectError(assoc(__, __, obj)(__)('bar')); +expectType(assoc(__, __, obj)('str')('bar')); +expectType & Record<'str', number>>(assoc(__, __, obj)('str')(2)); +expectType & Record<'str', string>>(assoc(__, __, {} as { other: string })('str')('bar')); + +// assoc(__, __, obj)(__, val)(key) +expectType(assoc(__, __, obj)(__, 'bar')('str')); +expectType & Record<'str', number>>(assoc(__, __, obj)(__, 2)('str')); +expectType & Record<'str', string>>(assoc(__, __, {} as { other: string })(__, 'bar')('str')); + +// assoc(__, __, obj)(key, val) +expectType(assoc(__, __, obj)('str', 'bar')); +expectType & Record<'str', number>>(assoc(__, __, obj)('str', 2)); +expectType & Record<'str', string>>(assoc(__, __, {} as { other: string })('str', 'bar')); diff --git a/types/assoc.d.ts b/types/assoc.d.ts index 0c01d815..cf9cf898 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -44,18 +44,14 @@ export function assoc(prop: K extends Placeholder ? ne // assoc (__, __, obj) export function assoc(__: Placeholder, __2: Placeholder, obj: U): { - // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is same type - (prop: K, val: U[K]): U; - // assoc(__, __, obj)(prop, val) when prop is keyof obj and val is not same type - (prop: K, val: T extends Placeholder ? never : T): Omit & Record; - // assoc(__, __, obj)(prop, val) when prop is not keyof obj - (prop: K extends Placeholder ? never : K, val: T extends Placeholder ? never : T): U & Record; + // assoc(__, __, obj)(prop)(val) + (prop: K extends Placeholder ? never : K): (val: T) => AssocResults; + // assoc(__, __, obj)(__, val)(prop) - (__: Placeholder, val: T extends Placeholder ? never : T): (prop: K) => T extends U[K] ? U : Omit & Record; - // assoc(__, __, obj)(prop)(val) prop is keyof obj - (prop: K): (val: T extends Placeholder ? never : T) => T extends U[K] ? U : Omit & Record; - // assoc(__, __, obj)(prop)(val) prop is not keyof obj - (prop: K extends Placeholder ? never : K): (val: T extends Placeholder ? never : T) => U & Record; + (__: Placeholder, val: T): (key: K) => AssocResults; + + // assoc(__, __, obj)(prop, val) + (prop: K, val: T): AssocResults; }; // assoc(prop, __, obj)(val), when prop is not keyof obj From 0e1d66a115eaf9a2a7519751bb1f5038ded17691 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Thu, 25 May 2023 02:25:33 -0600 Subject: [PATCH 09/20] and I think we're good! --- test/assoc.test.ts | 23 ++++++++++++++++++++++- types/assoc.d.ts | 24 ++++++++++-------------- 2 files changed, 32 insertions(+), 15 deletions(-) diff --git a/test/assoc.test.ts b/test/assoc.test.ts index d1aa8df8..7cfad33f 100644 --- a/test/assoc.test.ts +++ b/test/assoc.test.ts @@ -13,6 +13,7 @@ const obj: BasicObj = { str: 'foo', num: 1 }; // // assoc(key)(__, obj)(val) +expectError(assoc(__)); expectType(assoc('str')(__, obj)('bar')); expectType & Record<'str', number>>(assoc('str')(__, obj)(2)); expectType & Record<'str', string>>(assoc('str')(__, {} as { other: string })('foo')); @@ -61,7 +62,7 @@ expectType & Record<'str', string>>(assoc('str', // // assoc(__, __, obj)(key)(val) -expectError(assoc(__, __, obj)(__)('bar')); +expectError(assoc(__, __, obj)(__)); expectType(assoc(__, __, obj)('str')('bar')); expectType & Record<'str', number>>(assoc(__, __, obj)('str')(2)); expectType & Record<'str', string>>(assoc(__, __, {} as { other: string })('str')('bar')); @@ -75,3 +76,23 @@ expectType & Record<'str', string>>(assoc(__, __, expectType(assoc(__, __, obj)('str', 'bar')); expectType & Record<'str', number>>(assoc(__, __, obj)('str', 2)); expectType & Record<'str', string>>(assoc(__, __, {} as { other: string })('str', 'bar')); + +// +// rest +// + +// assoc(__, val, obj)(prop) +expectError(assoc(__,'bar', obj)(__)); +expectType(assoc(__,'bar', obj)('str')); +expectType & Record<'str', number>>(assoc(__, 2, obj)('str')); +expectType & Record<'str', string>>(assoc(__, 'bar', {} as { other: string })('str')); + +// assoc(key, __, obj)(__, val) +expectType(assoc('str', __, obj)('bar')); +expectType & Record<'str', number>>(assoc('str', __, obj)(2)); +expectType & Record<'str', string>>(assoc('str', __, {} as { other: string })('bar')); + +// assoc(key, val, obj) +expectType(assoc('str', 'bar', obj)); +expectType & Record<'str', number>>(assoc('str', 2, obj)); +expectType & Record<'str', string>>(assoc('str', 'foo', {} as { other: string })); diff --git a/types/assoc.d.ts b/types/assoc.d.ts index cf9cf898..34a1f5f8 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -1,7 +1,7 @@ import { Placeholder, AssocResults } from './util/tools'; // assoc(prop) -export function assoc(prop: K): { +export function assoc(prop: K extends Placeholder ? never : K): { // assoc(prop)(val) (val: T): { // assoc(prop)(val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type @@ -38,7 +38,7 @@ export function assoc(__: Placeholder, val: T) : { // assoc(prop, val) export function assoc(prop: K extends Placeholder ? never : K, val: T): { - // assoc(prop, val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type + // assoc(prop, val)(obj) (obj: U): AssocResults; }; @@ -54,15 +54,11 @@ export function assoc(__: Placeholder, __2: Placeholder, obj: U): { (prop: K, val: T): AssocResults; }; -// assoc(prop, __, obj)(val), when prop is not keyof obj -export function assoc(prop: K extends Placeholder ? never : K, __: Placeholder, obj: U): (val: T) => U & Record; -// assoc(prop, __, obj)(val), when K is keyof obj, tests if val is typeof obj[prop] for best return type -export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => T extends U[K] ? U : Omit & Record; -// assoc(__, val, obj)(prop), this tests if prop is keyof obj and if val is typeof obj[prop] for best return type -export function assoc(__: Placeholder, val: T extends Placeholder ? never : T, obj: U): (prop: K) => K extends keyof U ? T extends U[K] ? U : Omit & Record : U & Record; -// assoc(prop, val, obj) when prop is not keyof obj -export function assoc(prop: K extends Placeholder ? never : K, val: T extends Placeholder ? never : T, obj: U): U & Record; -// assoc(prop, val, obj) when prop is keyof obj and val is not same type -export function assoc(prop: K, val: T extends Placeholder ? never : T, obj: U): Omit & Record; -// assoc(prop, val, obj) when prop is keyof obj and val is same type -export function assoc(prop: K, val: U[K], obj: U): U; +// assoc(__, val, obj)(prop) +export function assoc(__: Placeholder, val: T, obj: U): (prop: K extends Placeholder ? never : K) => AssocResults; + +// assoc(prop, __, obj)(val) +export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => AssocResults; + +// assoc(prop, val, obj) +export function assoc(prop: K, val: T, obj: U): AssocResults; From edd0ddd32a865cfa62e135f555ee42eec840ef3c Mon Sep 17 00:00:00 2001 From: harris-miller Date: Thu, 25 May 2023 02:27:45 -0600 Subject: [PATCH 10/20] some tests for map funcs --- test/assoc.test.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/assoc.test.ts b/test/assoc.test.ts index 7cfad33f..00d4dcc6 100644 --- a/test/assoc.test.ts +++ b/test/assoc.test.ts @@ -1,5 +1,5 @@ import { expectType, expectError } from 'tsd'; -import { __, assoc } from '../es'; +import { __, assoc, map } from '../es'; type BasicObj = { str: string; @@ -96,3 +96,14 @@ expectType & Record<'str', string>>(assoc('str', expectType(assoc('str', 'bar', obj)); expectType & Record<'str', number>>(assoc('str', 2, obj)); expectType & Record<'str', string>>(assoc('str', 'foo', {} as { other: string })); + + +// +// map tests for sanity +// + +expectType([obj].map(assoc('str', 'bar'))); +expectType<(Omit & Record<'str', number>)[]>([obj].map(assoc('str', 2))); + +expectType(map(assoc('str', 'bar'), [obj])); +expectType<(Omit & Record<'str', number>)[]>(map(assoc('str', 2), [obj])); From 3765081f57aafb227fe1c7f778c849bb86a2cfb8 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Tue, 4 Jul 2023 03:09:34 -0600 Subject: [PATCH 11/20] type safety! --- types/assoc.d.ts | 55 ++++++++++++++++--------------------------- types/util/tools.d.ts | 1 - 2 files changed, 20 insertions(+), 36 deletions(-) diff --git a/types/assoc.d.ts b/types/assoc.d.ts index 34a1f5f8..7530d04c 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -1,64 +1,49 @@ -import { Placeholder, AssocResults } from './util/tools'; +import { Placeholder } from './util/tools'; // assoc(prop) -export function assoc(prop: K extends Placeholder ? never : K): { - // assoc(prop)(val) - (val: T): { - // assoc(prop)(val)(obj), when obj has key prop, tests if val is typeof obj[prop] for best return type - (obj: U): AssocResults; - } +export function assoc(prop: K): { + // assoc(prop)(val)(obj) + (val: T): >(obj: T) => U; - // assoc(prop)(__, obj) - (__: Placeholder, obj: U): { - // assoc(prop)(__, obj)(val), when obj has key prop, tests if val is typeof obj[prop] for best return type - (val: T): AssocResults; - } + // assoc(prop)(__, obj)(val) + >(__: Placeholder, obj: U): (val: T) => U; // assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type - (val: T, obj: U): AssocResults; + , T>(val: T, obj: U): U; }; // assoc(__, val) export function assoc(__: Placeholder, val: T) : { - // assoc(__, val)(prop) - (prop: K extends Placeholder ? never : K): { - // assoc(__, val)(prop)(obj) - (obj: U): AssocResults; - } + // assoc(__, val)(prop)(obj) + (prop: K extends Placeholder ? never : K): >(obj: U) => U; - // assoc(__, val)(__, obj) - (__2: Placeholder, obj: U): { - // assoc(__, val)(__, obj)(prop) - (prop: K): AssocResults; - }; + // assoc(__, val)(__, obj)(prop) + (__2: Placeholder, obj: U): (prop: T extends U[K] ? K : never) => U; // assoc(__, val)(prop, obj) - (prop: K, obj: U): AssocResults; + , K extends keyof U>(prop: K, obj: U); }; -// assoc(prop, val) -export function assoc(prop: K extends Placeholder ? never : K, val: T): { - // assoc(prop, val)(obj) - (obj: U): AssocResults; -}; +// assoc(prop, val)(obj) +export function assoc(prop: K extends Placeholder ? never : K, val: T): >(obj: U) => U; // assoc (__, __, obj) export function assoc(__: Placeholder, __2: Placeholder, obj: U): { // assoc(__, __, obj)(prop)(val) - (prop: K extends Placeholder ? never : K): (val: T) => AssocResults; + (prop: K extends Placeholder ? never : K): (val: T) => U; // assoc(__, __, obj)(__, val)(prop) - (__: Placeholder, val: T): (key: K) => AssocResults; + (__: Placeholder, val: T): (key: T extends U[K] ? T : never) => U; // assoc(__, __, obj)(prop, val) - (prop: K, val: T): AssocResults; + (prop: K, val: T): U; }; // assoc(__, val, obj)(prop) -export function assoc(__: Placeholder, val: T, obj: U): (prop: K extends Placeholder ? never : K) => AssocResults; +export function assoc(__: Placeholder, val: T, obj: U): (prop: T extends U[K] ? T : never) => U; // assoc(prop, __, obj)(val) -export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => AssocResults; +export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => U; // assoc(prop, val, obj) -export function assoc(prop: K, val: T, obj: U): AssocResults; +export function assoc(prop: K, val: T, obj: U): U; diff --git a/types/util/tools.d.ts b/types/util/tools.d.ts index 9d70ad3f..6be08a90 100644 --- a/types/util/tools.d.ts +++ b/types/util/tools.d.ts @@ -505,7 +505,6 @@ export type WidenLiterals = * Alias for the common result type for the `assoc` function */ export type AssocResults = U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; - /** * Extract the types from an array * Works with Tuples, eg `ElementOf` => `'p1' | 'p2'` From 512919a2a874c53497091baffcc8ee0714859a34 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Tue, 4 Jul 2023 03:33:50 -0600 Subject: [PATCH 12/20] tests --- test/assoc.test.ts | 111 +++++++++++++++++++++++++++++++-------------- types/assoc.d.ts | 8 ++-- 2 files changed, 82 insertions(+), 37 deletions(-) diff --git a/test/assoc.test.ts b/test/assoc.test.ts index 00d4dcc6..9a0f20a4 100644 --- a/test/assoc.test.ts +++ b/test/assoc.test.ts @@ -13,20 +13,31 @@ const obj: BasicObj = { str: 'foo', num: 1 }; // // assoc(key)(__, obj)(val) -expectError(assoc(__)); expectType(assoc('str')(__, obj)('bar')); -expectType & Record<'str', number>>(assoc('str')(__, obj)(2)); -expectType & Record<'str', string>>(assoc('str')(__, {} as { other: string })('foo')); +// fails for wrong value type +expectError(assoc('str')(__, obj)(2)); +// fails if key not unknown +expectError(assoc('what')(__, obj)('bar')); +// Record works as expected +expectType>(assoc('what')(__, {} as Record)(2)); // assoc(key)(val, obj) expectType(assoc('str')('bar', obj)); -expectType & Record<'str', number>>(assoc('str')(2, obj)); -expectType & Record<'str', string>>(assoc('str')('foo', {} as { other: string })); +// fails for wrong value type +expectError(assoc('str')(2, obj)); +// fails if key not unknown +expectError(assoc('what')('bar', obj)); +// Record works as expected +expectType>(assoc('what')(2, {} as Record)); // assoc(key)(val)(obj) expectType(assoc('str')('bar')(obj)); -expectType & Record<'str', number>>(assoc('str')(2)(obj)); -expectType & Record<'str', string>>(assoc('str')('foo')({} as { other: string })); +// fails for wrong value type +expectError(assoc('str')(2)(obj)); +// fails if key not unknown +expectError(assoc('what')('foo')(obj)); +// Record works as expected +expectType>(assoc('what')(2)({} as Record)); // @@ -35,18 +46,30 @@ expectType & Record<'str', string>>(assoc('str')( // assoc(__, val)(key)(obj) expectType(assoc(__, 'bar')('str')(obj)); -expectType & Record<'str', number>>(assoc(__, 2)('str')(obj)); -expectType & Record<'str', string>>(assoc(__, 'bar')('str')({ other: 'whatever'})); +// fails for wrong value type +expectError(assoc(__, 2)('str')(obj)); +// fails if key not unknown +expectError(assoc(__, 'bar')('what')(obj)); +// Record works as expected +expectType>(assoc(__, 2)('what')({} as Record)); // assoc(__, val)(__, key)(obj) expectType(assoc(__, 'bar')(__, obj)('str')); -expectType & Record<'str', number>>(assoc(__, 2)(__, obj)('str')); -expectType & Record<'str', string>>(assoc(__, 'bar')(__, { other: 'whatever'})('str')); +// fails for wrong value type +expectError(assoc(__, 2)(__, obj)('str')); +// fails if key not unknown +expectError(assoc(__, 'bar')(__, obj)('what')); +// Record works as expected +expectType>(assoc(__, 2)(__, {} as Record)('str')); // assoc(__, val)(key, obj) expectType(assoc(__, 'bar')('str', obj)); -expectType & Record<'str', number>>(assoc(__, 2)('str', obj)); -expectType & Record<'str', string>>(assoc(__, 'bar')('str', { other: 'whatever'})); +// fails for wrong value type +expectError(assoc(__, 2)('str', obj)); +// fails if key not unknown +expectError(assoc(__, 'bar')('what', obj)); +// Record works as expected +expectType>(assoc(__, 2)('str', {} as Record)); // // assoc(key, val) @@ -54,56 +77,78 @@ expectType & Record<'str', string>>(assoc(__, 'ba // assoc(key, val)(obj) expectType(assoc('str', 'bar')(obj)); -expectType & Record<'str', number>>(assoc('str', 2)(obj)); -expectType & Record<'str', string>>(assoc('str', 'foo')({} as { other: string })); +// fails for wrong value type +expectError(assoc('str', 2)(obj)); +// fails if key not unknown +expectError(assoc('what', 'bar')(obj)); +// Record works as expected +expectType>(assoc('str', 2)({} as Record)); // // assoc__, __, obj) // // assoc(__, __, obj)(key)(val) -expectError(assoc(__, __, obj)(__)); expectType(assoc(__, __, obj)('str')('bar')); -expectType & Record<'str', number>>(assoc(__, __, obj)('str')(2)); -expectType & Record<'str', string>>(assoc(__, __, {} as { other: string })('str')('bar')); +// fails for wrong value type +expectError(assoc(__, __, obj)('str')(2)); +// fails if key not unknown +expectError(assoc(__, __, obj)('what')('bar')); +// Record works as expected +expectType>(assoc(__, __, {} as Record)('str')(2)); // assoc(__, __, obj)(__, val)(key) expectType(assoc(__, __, obj)(__, 'bar')('str')); -expectType & Record<'str', number>>(assoc(__, __, obj)(__, 2)('str')); -expectType & Record<'str', string>>(assoc(__, __, {} as { other: string })(__, 'bar')('str')); +// fails for wrong value type +expectError(assoc(__, __, obj)(__, 2)('str')); +// fails if key not unknown +expectError(assoc(__, __, obj)(__, 'bar')('what')); +// Record works as expected +expectType>(assoc(__, __, {} as Record)(__, 2)('str')); // assoc(__, __, obj)(key, val) expectType(assoc(__, __, obj)('str', 'bar')); -expectType & Record<'str', number>>(assoc(__, __, obj)('str', 2)); -expectType & Record<'str', string>>(assoc(__, __, {} as { other: string })('str', 'bar')); +// fails for wrong value type +expectError(assoc(__, __, obj)('str', 2)); +// fails if key not unknown +expectError(assoc(__, __, obj)('what', 'bar')); +// Record works as expected +expectType>(assoc(__, __, {} as Record)('str', 2)); // // rest // // assoc(__, val, obj)(prop) -expectError(assoc(__,'bar', obj)(__)); expectType(assoc(__,'bar', obj)('str')); -expectType & Record<'str', number>>(assoc(__, 2, obj)('str')); -expectType & Record<'str', string>>(assoc(__, 'bar', {} as { other: string })('str')); +// fails for wrong value type +expectError(assoc(__, 2, obj)('str')); +// fails if key not unknown +expectError(assoc(__, 'bar', obj)('what')); +// Record works as expected +expectType>(assoc(__, 2, {} as Record)('str')); // assoc(key, __, obj)(__, val) expectType(assoc('str', __, obj)('bar')); -expectType & Record<'str', number>>(assoc('str', __, obj)(2)); -expectType & Record<'str', string>>(assoc('str', __, {} as { other: string })('bar')); +// fails for wrong value type +expectError(assoc('str', __, obj)(2)); +// fails if key not unknown +expectError(assoc('what', __, obj)('bar')); +// Record works as expected +expectType>(assoc('str', __, {} as Record)(2)); // assoc(key, val, obj) expectType(assoc('str', 'bar', obj)); -expectType & Record<'str', number>>(assoc('str', 2, obj)); -expectType & Record<'str', string>>(assoc('str', 'foo', {} as { other: string })); - +// fails for wrong value type +expectError(assoc('str', 2, obj)); +// fails if key not unknown +expectError(assoc('what', 'bar', obj)); +// Record works as expected +expectType>(assoc('str', 2, {} as Record)); // // map tests for sanity // expectType([obj].map(assoc('str', 'bar'))); -expectType<(Omit & Record<'str', number>)[]>([obj].map(assoc('str', 2))); - expectType(map(assoc('str', 'bar'), [obj])); -expectType<(Omit & Record<'str', number>)[]>(map(assoc('str', 2), [obj])); diff --git a/types/assoc.d.ts b/types/assoc.d.ts index 7530d04c..853f670a 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -3,7 +3,7 @@ import { Placeholder } from './util/tools'; // assoc(prop) export function assoc(prop: K): { // assoc(prop)(val)(obj) - (val: T): >(obj: T) => U; + (val: T): >(obj: U) => U; // assoc(prop)(__, obj)(val) >(__: Placeholder, obj: U): (val: T) => U; @@ -21,7 +21,7 @@ export function assoc(__: Placeholder, val: T) : { (__2: Placeholder, obj: U): (prop: T extends U[K] ? K : never) => U; // assoc(__, val)(prop, obj) - , K extends keyof U>(prop: K, obj: U); + , K extends keyof U>(prop: K, obj: U): U; }; // assoc(prop, val)(obj) @@ -33,14 +33,14 @@ export function assoc(__: Placeholder, __2: Placeholder, obj: U): { (prop: K extends Placeholder ? never : K): (val: T) => U; // assoc(__, __, obj)(__, val)(prop) - (__: Placeholder, val: T): (key: T extends U[K] ? T : never) => U; + (__: Placeholder, val: T): (prop: T extends U[K] ? K : never) => U; // assoc(__, __, obj)(prop, val) (prop: K, val: T): U; }; // assoc(__, val, obj)(prop) -export function assoc(__: Placeholder, val: T, obj: U): (prop: T extends U[K] ? T : never) => U; +export function assoc(__: Placeholder, val: T, obj: U): (prop: T extends U[K] ? K : never) => U; // assoc(prop, __, obj)(val) export function assoc(prop: K, __: Placeholder, obj: U): (val: T) => U; From dd5482fcfcd62f7181245de3930557836af56367 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Tue, 4 Jul 2023 12:16:22 -0600 Subject: [PATCH 13/20] rename that type var --- test/assoc.test.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/test/assoc.test.ts b/test/assoc.test.ts index 9a0f20a4..b0493053 100644 --- a/test/assoc.test.ts +++ b/test/assoc.test.ts @@ -1,19 +1,19 @@ import { expectType, expectError } from 'tsd'; import { __, assoc, map } from '../es'; -type BasicObj = { +type Obj = { str: string; num: number; }; -const obj: BasicObj = { str: 'foo', num: 1 }; +const obj: Obj = { str: 'foo', num: 1 }; // // assoc(key) // // assoc(key)(__, obj)(val) -expectType(assoc('str')(__, obj)('bar')); +expectType(assoc('str')(__, obj)('bar')); // fails for wrong value type expectError(assoc('str')(__, obj)(2)); // fails if key not unknown @@ -22,7 +22,7 @@ expectError(assoc('what')(__, obj)('bar')); expectType>(assoc('what')(__, {} as Record)(2)); // assoc(key)(val, obj) -expectType(assoc('str')('bar', obj)); +expectType(assoc('str')('bar', obj)); // fails for wrong value type expectError(assoc('str')(2, obj)); // fails if key not unknown @@ -31,7 +31,7 @@ expectError(assoc('what')('bar', obj)); expectType>(assoc('what')(2, {} as Record)); // assoc(key)(val)(obj) -expectType(assoc('str')('bar')(obj)); +expectType(assoc('str')('bar')(obj)); // fails for wrong value type expectError(assoc('str')(2)(obj)); // fails if key not unknown @@ -45,7 +45,7 @@ expectType>(assoc('what')(2)({} as Record // // assoc(__, val)(key)(obj) -expectType(assoc(__, 'bar')('str')(obj)); +expectType(assoc(__, 'bar')('str')(obj)); // fails for wrong value type expectError(assoc(__, 2)('str')(obj)); // fails if key not unknown @@ -54,7 +54,7 @@ expectError(assoc(__, 'bar')('what')(obj)); expectType>(assoc(__, 2)('what')({} as Record)); // assoc(__, val)(__, key)(obj) -expectType(assoc(__, 'bar')(__, obj)('str')); +expectType(assoc(__, 'bar')(__, obj)('str')); // fails for wrong value type expectError(assoc(__, 2)(__, obj)('str')); // fails if key not unknown @@ -63,7 +63,7 @@ expectError(assoc(__, 'bar')(__, obj)('what')); expectType>(assoc(__, 2)(__, {} as Record)('str')); // assoc(__, val)(key, obj) -expectType(assoc(__, 'bar')('str', obj)); +expectType(assoc(__, 'bar')('str', obj)); // fails for wrong value type expectError(assoc(__, 2)('str', obj)); // fails if key not unknown @@ -76,7 +76,7 @@ expectType>(assoc(__, 2)('str', {} as Record(assoc('str', 'bar')(obj)); +expectType(assoc('str', 'bar')(obj)); // fails for wrong value type expectError(assoc('str', 2)(obj)); // fails if key not unknown @@ -89,7 +89,7 @@ expectType>(assoc('str', 2)({} as Record) // // assoc(__, __, obj)(key)(val) -expectType(assoc(__, __, obj)('str')('bar')); +expectType(assoc(__, __, obj)('str')('bar')); // fails for wrong value type expectError(assoc(__, __, obj)('str')(2)); // fails if key not unknown @@ -98,7 +98,7 @@ expectError(assoc(__, __, obj)('what')('bar')); expectType>(assoc(__, __, {} as Record)('str')(2)); // assoc(__, __, obj)(__, val)(key) -expectType(assoc(__, __, obj)(__, 'bar')('str')); +expectType(assoc(__, __, obj)(__, 'bar')('str')); // fails for wrong value type expectError(assoc(__, __, obj)(__, 2)('str')); // fails if key not unknown @@ -107,7 +107,7 @@ expectError(assoc(__, __, obj)(__, 'bar')('what')); expectType>(assoc(__, __, {} as Record)(__, 2)('str')); // assoc(__, __, obj)(key, val) -expectType(assoc(__, __, obj)('str', 'bar')); +expectType(assoc(__, __, obj)('str', 'bar')); // fails for wrong value type expectError(assoc(__, __, obj)('str', 2)); // fails if key not unknown @@ -120,7 +120,7 @@ expectType>(assoc(__, __, {} as Record)(' // // assoc(__, val, obj)(prop) -expectType(assoc(__,'bar', obj)('str')); +expectType(assoc(__,'bar', obj)('str')); // fails for wrong value type expectError(assoc(__, 2, obj)('str')); // fails if key not unknown @@ -129,7 +129,7 @@ expectError(assoc(__, 'bar', obj)('what')); expectType>(assoc(__, 2, {} as Record)('str')); // assoc(key, __, obj)(__, val) -expectType(assoc('str', __, obj)('bar')); +expectType(assoc('str', __, obj)('bar')); // fails for wrong value type expectError(assoc('str', __, obj)(2)); // fails if key not unknown @@ -138,7 +138,7 @@ expectError(assoc('what', __, obj)('bar')); expectType>(assoc('str', __, {} as Record)(2)); // assoc(key, val, obj) -expectType(assoc('str', 'bar', obj)); +expectType(assoc('str', 'bar', obj)); // fails for wrong value type expectError(assoc('str', 2, obj)); // fails if key not unknown @@ -150,5 +150,5 @@ expectType>(assoc('str', 2, {} as Record) // map tests for sanity // -expectType([obj].map(assoc('str', 'bar'))); -expectType(map(assoc('str', 'bar'), [obj])); +expectType([obj].map(assoc('str', 'bar'))); +expectType(map(assoc('str', 'bar'), [obj])); From 273b92800704bd0b6529f6af8c68e4e7ee7c99db Mon Sep 17 00:00:00 2001 From: harris-miller Date: Tue, 4 Jul 2023 13:04:27 -0600 Subject: [PATCH 14/20] update dissoc as well with new tests --- test/assoc.test.ts | 9 +------ test/dissoc.test.ts | 65 +++++++++++++++++++++++++++++++++++++++++++++ types/dissoc.d.ts | 8 ++++-- 3 files changed, 72 insertions(+), 10 deletions(-) create mode 100644 test/dissoc.test.ts diff --git a/test/assoc.test.ts b/test/assoc.test.ts index b0493053..324b1cc4 100644 --- a/test/assoc.test.ts +++ b/test/assoc.test.ts @@ -1,5 +1,5 @@ import { expectType, expectError } from 'tsd'; -import { __, assoc, map } from '../es'; +import { __, assoc } from '../es'; type Obj = { str: string; @@ -145,10 +145,3 @@ expectError(assoc('str', 2, obj)); expectError(assoc('what', 'bar', obj)); // Record works as expected expectType>(assoc('str', 2, {} as Record)); - -// -// map tests for sanity -// - -expectType([obj].map(assoc('str', 'bar'))); -expectType(map(assoc('str', 'bar'), [obj])); diff --git a/test/dissoc.test.ts b/test/dissoc.test.ts new file mode 100644 index 00000000..639d1ed6 --- /dev/null +++ b/test/dissoc.test.ts @@ -0,0 +1,65 @@ +import { expectType, expectError } from 'tsd'; +import { __, dissoc } from '../es'; + +// `dissoc` does a `delete obj.key` under the hood, so the behavior for `dissoc` should make that +type Obj = { + str: string; + num: number; + opt?: boolean; + orUndefined: boolean | undefined; + orNull: boolean | null; +}; + +const obj: Obj = { str: 'foo', num: 1, orUndefined: true, orNull: true }; + +// must mark failed operations with ts-expect-error, otherwise `npm run test` fails +// @ts-expect-error +delete obj.str; +// @ts-expect-error +delete obj.num; +delete obj.opt; +delete obj.orUndefined; +// @ts-expect-error +delete obj.orNull; + +// only `opt` and `orUndefined` are allowed operations +// `dissoc` should match that behavior + +expectError(dissoc('str', obj)); +expectError(dissoc('num', obj)); +expectType(dissoc('opt', obj)); +expectType(dissoc('orUndefined', obj)); +expectError(dissoc('orNull', obj)); + +expectError(dissoc('str')(obj)); +expectError(dissoc('num')(obj)); +expectType(dissoc('opt')(obj)); +expectType(dissoc('orUndefined')(obj)); +expectError(dissoc('orNull')(obj)); + +expectError(dissoc(__, obj)('str')); +expectError(dissoc(__, obj)('num')); +expectType(dissoc(__, obj)('opt')); +expectType(dissoc(__, obj)('orUndefined')); +expectError(dissoc(__, obj)('orNull')); + +// Record is allowed +const rec: Record = { foo: 1, bar: 2 }; + +// delete operation is ok for all keys +delete rec.foo; +delete rec.bar; +delete rec.unknownKey; + +// and so are for `dissoc` +dissoc('foo', rec); +dissoc('bar', rec); +dissoc('unknownKey', rec); + +dissoc('foo')(rec); +dissoc('bar')(rec); +dissoc('unknownKey')(rec); + +dissoc(__, rec)('foo'); +dissoc(__, rec)('bar'); +dissoc(__, rec)('unknownKey'); diff --git a/types/dissoc.d.ts b/types/dissoc.d.ts index 08533a4d..c7b9fd24 100644 --- a/types/dissoc.d.ts +++ b/types/dissoc.d.ts @@ -1,2 +1,6 @@ -export function dissoc(prop: K): (obj: T) => Omit; -export function dissoc(prop: K, obj: T): Omit; +import { Placeholder } from './util/tools'; + +// `string extends keyof U` is to support `Record` +export function dissoc(prop: K): (obj: string extends keyof U ? U : undefined extends U[K] ? U : never) => U; +export function dissoc(__: Placeholder, obj: U): (prop: string extends keyof U ? K : undefined extends U[K] ? K : never) => U; +export function dissoc(prop: string extends keyof U ? K : undefined extends U[K] ? K : never, obj: U): U; From 8f4220d34bc5b14a2f3fe1b3c1c7e2293b4a149f Mon Sep 17 00:00:00 2001 From: harris-miller Date: Tue, 4 Jul 2023 13:48:44 -0600 Subject: [PATCH 15/20] dissollow placeholder in that way --- types/dissoc.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/dissoc.d.ts b/types/dissoc.d.ts index c7b9fd24..457a83a2 100644 --- a/types/dissoc.d.ts +++ b/types/dissoc.d.ts @@ -1,6 +1,6 @@ import { Placeholder } from './util/tools'; // `string extends keyof U` is to support `Record` -export function dissoc(prop: K): (obj: string extends keyof U ? U : undefined extends U[K] ? U : never) => U; +export function dissoc(prop: K extends Placeholder ? never : K): (obj: string extends keyof U ? U : undefined extends U[K] ? U : never) => U; export function dissoc(__: Placeholder, obj: U): (prop: string extends keyof U ? K : undefined extends U[K] ? K : never) => U; export function dissoc(prop: string extends keyof U ? K : undefined extends U[K] ? K : never, obj: U): U; From e7c788cf485f427c800d098293b021a85a53fe8d Mon Sep 17 00:00:00 2001 From: harris-miller Date: Wed, 5 Jul 2023 03:41:40 -0600 Subject: [PATCH 16/20] update modify a bit --- types/modify.d.ts | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/types/modify.d.ts b/types/modify.d.ts index fe9a2295..949a064e 100644 --- a/types/modify.d.ts +++ b/types/modify.d.ts @@ -1,10 +1,8 @@ -export function modify( - prop: K, - fn: (a: A) => P, -): >(target: T) => Omit & Record; +// modify(prop)(fn)(obj) +export function modify(prop: K):(fn: (value: T) => T) => >(object: U) => U; -export function modify( - prop: K, - fn: (a: T[K]) => P, - obj: T, -): Omit & Record; +// modify(prop, fn)(obj) +export function modify(prop: K, fn: (value: T) => T): >(object: U) => U; + +// modify(prop, fn, obj) +export function modify(prop: K, fn: (value: U[K]) => U[K], object: U): U; From 6c5a627c2c518ff555c7eccaebbd658ed98cfa87 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Wed, 5 Jul 2023 19:41:39 -0600 Subject: [PATCH 17/20] correct overloaded return for modify(prop), not adding Placeholder versions yet --- types/modify.d.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/types/modify.d.ts b/types/modify.d.ts index 949a064e..28c07378 100644 --- a/types/modify.d.ts +++ b/types/modify.d.ts @@ -1,5 +1,10 @@ -// modify(prop)(fn)(obj) -export function modify(prop: K):(fn: (value: T) => T) => >(object: U) => U; +// modify(prop) +export function modify(prop: K): { + // modify(prop)(fn)(obj) + (fn: (value: T) => T): >(object: U) => U; + // modify(prop)(fn), obj) + >(fn: (value: T) => T, object: U): U; +}; // modify(prop, fn)(obj) export function modify(prop: K, fn: (value: T) => T): >(object: U) => U; From bc608083ba750d6577ebb4ba177053517af3e11d Mon Sep 17 00:00:00 2001 From: harris-miller Date: Wed, 5 Jul 2023 19:54:08 -0600 Subject: [PATCH 18/20] modify tests --- test/modify.test.ts | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 test/modify.test.ts diff --git a/test/modify.test.ts b/test/modify.test.ts new file mode 100644 index 00000000..dbf37d0c --- /dev/null +++ b/test/modify.test.ts @@ -0,0 +1,34 @@ +import { expectError, expectType } from 'tsd'; + +import { modify, toUpper, add, identity, pipe, map } from '../es'; + +type Obj = { + foo: string; + bar: number; +}; + +expectType(modify('foo', toUpper, {} as Obj)); +expectType(modify('bar', add(1), {} as Obj)); +expectType(modify('foo', toUpper)({} as Obj)); +expectType(modify('bar', add(1))({} as Obj)); +expectType(modify('foo')(toUpper)({} as Obj)); +expectType(modify('bar')(add(1))({} as Obj)); +expectType(modify('foo')(toUpper, {} as Obj)); +expectType(modify('bar')(add(1), {} as Obj)); + +// fails when function has wrong argument type +expectError(modify('foo', add(1), {} as Obj)); +expectError(modify('bar', toUpper, {} as Obj)); + +// fails when key does not exist on Obj +expectError(modify('unknownKey', toUpper, {} as Obj)); + +// works with generic fn +expectType(modify('foo', identity, {} as Obj)); + +// pipe and map sanity checks +const f = pipe( + map(modify('foo', toUpper)) +); + +expectType(f([] as Obj[])); From 706f88df0f6935759dcfe254262a71598e49dbe2 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Sun, 8 Oct 2023 14:37:29 -0600 Subject: [PATCH 19/20] fix after rebase --- types/util/tools.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/util/tools.d.ts b/types/util/tools.d.ts index 6be08a90..9d70ad3f 100644 --- a/types/util/tools.d.ts +++ b/types/util/tools.d.ts @@ -505,6 +505,7 @@ export type WidenLiterals = * Alias for the common result type for the `assoc` function */ export type AssocResults = U extends Record ? T extends U[K] ? U : Omit & Record : Omit & Record; + /** * Extract the types from an array * Works with Tuples, eg `ElementOf` => `'p1' | 'p2'` From e6b7256a536b2a362a70671b089389cea38a6642 Mon Sep 17 00:00:00 2001 From: harris-miller Date: Sun, 8 Oct 2023 15:03:18 -0600 Subject: [PATCH 20/20] better comment for dissoc --- types/dissoc.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/dissoc.d.ts b/types/dissoc.d.ts index 457a83a2..3d1d6800 100644 --- a/types/dissoc.d.ts +++ b/types/dissoc.d.ts @@ -1,6 +1,6 @@ import { Placeholder } from './util/tools'; -// `string extends keyof U` is to support `Record` +// `string extends keyof U` is true only for `Record`, where the keys are not known, something that still needs to be supported export function dissoc(prop: K extends Placeholder ? never : K): (obj: string extends keyof U ? U : undefined extends U[K] ? U : never) => U; export function dissoc(__: Placeholder, obj: U): (prop: string extends keyof U ? K : undefined extends U[K] ? K : never) => U; export function dissoc(prop: string extends keyof U ? K : undefined extends U[K] ? K : never, obj: U): U;