From 5cae6026129337c767ecc61ecee7bdd09664e48d Mon Sep 17 00:00:00 2001 From: Harris Miller Date: Mon, 19 Feb 2024 16:49:47 -0700 Subject: [PATCH] fix(assoc) overload reorder and unit tests (#96) --- test/assoc.test.ts | 147 +++++++++++++++++++++++++++++++++++++++++++++ types/assoc.d.ts | 110 ++++++++++++++++++++------------- 2 files changed, 214 insertions(+), 43 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 0000000..f64ecc6 --- /dev/null +++ b/test/assoc.test.ts @@ -0,0 +1,147 @@ +import { expectType } from 'tsd'; +import { __, assoc } from '../es'; + +type Obj = { + str: string; + num: number; +}; + +const obj: Obj = { str: 'foo', num: 1 }; + +// +// assoc(key) +// + +// assoc(key)(__, obj)(val) +expectType(assoc('str')(__, obj)('bar')); +// updates value type +expectType & Record<'str', number>>(assoc('str')(__, obj)(2)); +// adds key/value pair +expectType>(assoc('what')(__, obj)('bar')); +// Record works as expected +expectType>(assoc('what')(__, {} as Record)(2)); + +// assoc(key)(val, obj) +expectType(assoc('str')('bar', obj)); +// updates value type +expectType & Record<'str', number>>(assoc('str')(2, obj)); +// adds key/value pair +expectType>(assoc('what')('bar', obj)); +// Record works as expected +expectType>(assoc('what')(2, {} as Record)); + +// assoc(key)(val)(obj) +expectType(assoc('str')('bar')(obj)); +// updates value type +expectType & Record<'str', number>>(assoc('str')(2)(obj)); +// adds key/value pair +expectType>(assoc('what')('foo')(obj)); +// Record works as expected +expectType>(assoc('what')(2)({} as Record)); + + +// +// assoc(__, val) +// + +// assoc(__, val)(key)(obj) +expectType(assoc(__, 'bar')('str')(obj)); +// updates value type +expectType & Record<'str', number>>(assoc(__, 2)('str')(obj)); +// adds key/value pair +expectType>(assoc(__, 'bar')('what')(obj)); +// Record works as expected +expectType>(assoc(__, 2)('what')({} as Record)); + +// assoc(__, val)(__, key)(obj) +expectType(assoc(__, 'bar')(__, obj)('str')); +// updates value type +expectType & Record<'str', number>>(assoc(__, 2)(__, obj)('str')); +// adds key/value pair +expectType>(assoc(__, 'bar')(__, obj)('what')); +// Record works as expected +expectType>(assoc(__, 2)(__, {} as Record)('str')); + +// assoc(__, val)(key, obj) +expectType(assoc(__, 'bar')('str', obj)); +// updates value type +expectType & Record<'str', number>>(assoc(__, 2)('str', obj)); +// adds key/value pair +expectType>(assoc(__, 'bar')('what', obj)); +// Record works as expected +expectType>(assoc(__, 2)('str', {} as Record)); + +// +// assoc(key, val) +// + +// assoc(key, val)(obj) +expectType(assoc('str', 'bar')(obj)); +// updates value type +expectType & Record<'str', number>>(assoc('str', 2)(obj)); +// adds key/value pair +expectType>(assoc('what', 'bar')(obj)); +// Record works as expected +expectType>(assoc('str', 2)({} as Record)); + +// +// assoc__, __, obj) +// + +// assoc(__, __, obj)(key)(val) +expectType(assoc(__, __, obj)('str')('bar')); +// updates value type +expectType & Record<'str', number>>(assoc(__, __, obj)('str')(2)); +// adds key/value pair +expectType>(assoc(__, __, obj)('what')('bar')); +// Record works as expected +expectType>(assoc(__, __, {} as Record)('str')(2)); + +// assoc(__, __, obj)(__, val)(key) +expectType(assoc(__, __, obj)(__, 'bar')('str')); +// updates value type +expectType & Record<'str', number>>(assoc(__, __, obj)(__, 2)('str')); +// adds key/value pair +expectType>(assoc(__, __, obj)(__, 'bar')('what')); +// Record works as expected +expectType>(assoc(__, __, {} as Record)(__, 2)('str')); + +// assoc(__, __, obj)(key, val) +expectType(assoc(__, __, obj)('str', 'bar')); +// updates value type +expectType & Record<'str', number>>(assoc(__, __, obj)('str', 2)); +// adds key/value pair +expectType>(assoc(__, __, obj)('what', 'bar')); +// Record works as expected +expectType>(assoc(__, __, {} as Record)('str', 2)); + +// +// rest +// + +// assoc(__, val, obj)(prop) +expectType(assoc(__,'bar', obj)('str')); +// updates value type +expectType & Record<'str', number>>(assoc(__, 2, obj)('str')); +// adds key/value pair +expectType>(assoc(__, 'bar', obj)('what')); +// Record works as expected +expectType>(assoc(__, 2, {} as Record)('str')); + +// assoc(key, __, obj)(__, val) +expectType(assoc('str', __, obj)('bar')); +// updates value type +expectType & Record<'str', number>>(assoc('str', __, obj)(2)); +// adds key/value pair +expectType>(assoc('what', __, obj)('bar')); +// Record works as expected +expectType>(assoc('str', __, {} as Record)(2)); + +// assoc(key, val, obj) +expectType(assoc('str', 'bar', obj)); +// updates value type +expectType & Record<'str', number>>(assoc('str', 2, obj)); +// adds key/value pair +expectType>(assoc('what', 'bar', obj)); +// Record works as expected +expectType>(assoc('str', 2, {} as Record)); diff --git a/types/assoc.d.ts b/types/assoc.d.ts index 1814c87..cebbd9d 100644 --- a/types/assoc.d.ts +++ b/types/assoc.d.ts @@ -1,20 +1,43 @@ 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; -// assoc(prop, val, obj) when prop is not keyof obj -export function assoc(prop: K, val: T, obj: U): U & Record; +// assoc(prop) +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)(__, 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) 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(__, val) export function assoc(__: Placeholder, val: T) : { + // assoc(__, val)(prop) + (prop: 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; + // 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 @@ -26,14 +49,6 @@ export function assoc(__: Placeholder, val: T) : { (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): { - // 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; - // assoc(__, val)(prop)(obj) when obj does not have key prop - (obj: U): U & Record; - } }; // assoc(prop, val) @@ -44,30 +59,39 @@ export function assoc(prop: K, val: T) : { (obj: U): U & Record; }; -// assoc(prop) -export function assoc(prop: K): { - // assoc(prop)(__, obj) when prop is keyof obj - >(__: Placeholder, obj: U): { - // assoc(prop)(__, obj)(val) if val is typeof obj[prop] +// assoc(__, __, obj) +export function assoc(__: Placeholder, __2: Placeholder, obj: U): { + // assoc(__, __, obj)(key) + (key: K): { + // assoc(__, __, obj)(key)(value), when K is keyof U and value types match (val: T): U; - // assoc(prop)(__, obj)(val) if val is not typeof obj[prop] - (val: T): Record & Omit; + // assoc(__, __, obj)(key)(value), when K is keyof U and value types do not match + (val: T): Record & Omit } - // 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] - >(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(__, __, obj)(key)(value), when K is not keyof U + (key: K): (val: T) => U & Record; + // assoc(__, __, obj)(__, value) + (__: Placeholder, val: T): { + // assoc(__, __, obj)(__, value)(key), when obj has key prop, tests if val is typeof obj[prop] for best return type + (key: K): U[K] extends T ? U : Record & Omit; + // assoc(__, __, obj)(__, value)(key) when obj does not have key prop + (key: K): U & Record; } + // assoc(__, __, obj)(key, value) + (key: K, val: U[K]): U; + (key: K, val: T): Record & Omit; + (key: K, val: T): 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, 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; +// assoc(prop, val, obj) when prop is not keyof obj +export function assoc(prop: K, val: T, obj: U): U & Record;