Skip to content

Commit

Permalink
fix(assoc) overload reorder and unit tests (#96)
Browse files Browse the repository at this point in the history
  • Loading branch information
Harris-Miller authored Feb 19, 2024
1 parent 468e104 commit 5cae602
Show file tree
Hide file tree
Showing 2 changed files with 214 additions and 43 deletions.
147 changes: 147 additions & 0 deletions test/assoc.test.ts
Original file line number Diff line number Diff line change
@@ -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<Obj>(assoc('str')(__, obj)('bar'));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc('str')(__, obj)(2));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc('what')(__, obj)('bar'));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc('what')(__, {} as Record<string, number>)(2));

// assoc(key)(val, obj)
expectType<Obj>(assoc('str')('bar', obj));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc('str')(2, obj));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc('what')('bar', obj));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc('what')(2, {} as Record<string, number>));

// assoc(key)(val)(obj)
expectType<Obj>(assoc('str')('bar')(obj));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc('str')(2)(obj));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc('what')('foo')(obj));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc('what')(2)({} as Record<string, number>));


//
// assoc(__, val)
//

// assoc(__, val)(key)(obj)
expectType<Obj>(assoc(__, 'bar')('str')(obj));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc(__, 2)('str')(obj));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc(__, 'bar')('what')(obj));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc(__, 2)('what')({} as Record<string, number>));

// assoc(__, val)(__, key)(obj)
expectType<Obj>(assoc(__, 'bar')(__, obj)('str'));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc(__, 2)(__, obj)('str'));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc(__, 'bar')(__, obj)('what'));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc(__, 2)(__, {} as Record<string, number>)('str'));

// assoc(__, val)(key, obj)
expectType<Obj>(assoc(__, 'bar')('str', obj));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc(__, 2)('str', obj));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc(__, 'bar')('what', obj));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc(__, 2)('str', {} as Record<string, number>));

//
// assoc(key, val)
//

// assoc(key, val)(obj)
expectType<Obj>(assoc('str', 'bar')(obj));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc('str', 2)(obj));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc('what', 'bar')(obj));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc('str', 2)({} as Record<string, number>));

//
// assoc__, __, obj)
//

// assoc(__, __, obj)(key)(val)
expectType<Obj>(assoc(__, __, obj)('str')('bar'));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc(__, __, obj)('str')(2));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc(__, __, obj)('what')('bar'));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc(__, __, {} as Record<string, number>)('str')(2));

// assoc(__, __, obj)(__, val)(key)
expectType<Obj>(assoc(__, __, obj)(__, 'bar')('str'));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc(__, __, obj)(__, 2)('str'));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc(__, __, obj)(__, 'bar')('what'));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc(__, __, {} as Record<string, number>)(__, 2)('str'));

// assoc(__, __, obj)(key, val)
expectType<Obj>(assoc(__, __, obj)('str', 'bar'));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc(__, __, obj)('str', 2));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc(__, __, obj)('what', 'bar'));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc(__, __, {} as Record<string, number>)('str', 2));

//
// rest
//

// assoc(__, val, obj)(prop)
expectType<Obj>(assoc(__,'bar', obj)('str'));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc(__, 2, obj)('str'));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc(__, 'bar', obj)('what'));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc(__, 2, {} as Record<string, number>)('str'));

// assoc(key, __, obj)(__, val)
expectType<Obj>(assoc('str', __, obj)('bar'));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc('str', __, obj)(2));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc('what', __, obj)('bar'));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc('str', __, {} as Record<string, number>)(2));

// assoc(key, val, obj)
expectType<Obj>(assoc('str', 'bar', obj));
// updates value type
expectType<Omit<Obj, 'str'> & Record<'str', number>>(assoc('str', 2, obj));
// adds key/value pair
expectType<Obj & Record<'what', string>>(assoc('what', 'bar', obj));
// Record<string, number> works as expected
expectType<Record<string, number>>(assoc('str', 2, {} as Record<string, number>));
110 changes: 67 additions & 43 deletions types/assoc.d.ts
Original file line number Diff line number Diff line change
@@ -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<T, U>(__: Placeholder, val: T, obj: U): <K extends string>(prop: K) => K extends keyof U ? T extends U[K] ? U : Record<K, T> & Omit<U, K> : U & Record<K, T>;
// assoc(prop, __, obj)(val), when K is keyof obj, tests if val is typeof obj[prop] for best return type
export function assoc<U, K extends keyof U>(prop: K, __: Placeholder, obj: U): <T>(val: T) => T extends U[K] ? U : Record<K, T> & Omit<U, K>;
// assoc(prop, __, obj)(val), when prop is not keyof obj
export function assoc<U, K extends string>(prop: K, __: Placeholder, obj: U): <T>(val: T) => U & Record<K, T>;
// assoc(prop, val, obj) when prop is keyof obj and val is same type
export function assoc<K extends keyof U, U>(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<T, K extends keyof U, U>(prop: K, val: T, obj: U): Record<K, T> & Omit<U, K>;
// assoc(prop, val, obj) when prop is not keyof obj
export function assoc<T, U, K extends string>(prop: K, val: T, obj: U): U & Record<K, T>;
// assoc(prop)
export function assoc<K extends string>(prop: K): {
// assoc(prop)(val)
<T>(val: T): {
// assoc(prop)(val)(obj) when obj has key prop and val is typeof obj[prop]
<U extends Record<K, T>>(obj: U): U;
// assoc(prop)(val)(obj) when obj has key prop and val is not typeof obj[prop]
<U extends Record<K, any>>(obj: U): Record<K, T> & Omit<U, K>;
// assoc(prop)(val)(obj) when obj does not have key prop
<U>(obj: U): U & Record<K, T>;
}

// assoc(prop)(__, obj) when prop is keyof obj
<U extends Record<K, any>>(__: Placeholder, obj: U): {
// assoc(prop)(__, obj)(val) if val is typeof obj[prop]
<T extends U[K]>(val: T): U;
// assoc(prop)(__, obj)(val) if val is not typeof obj[prop]
<T>(val: T): Record<K, T> & Omit<U, K>;
}
// assoc(prop)(__, obj) when prop is not keyof obj
<U>(__: Placeholder, obj: U): <T>(val: T) => U & Record<K, T>;

// assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type
<T, U extends Record<K, any>>(val: T, obj: U): U[K] extends T ? U : Record<K, T> & Omit<U, K>;
// assoc(prop)(val, obj) when obj does not have a key prop
<T, U>(val: T, obj: U): U & Record<K, T>;
};

// assoc(__, val)
export function assoc<T>(__: Placeholder, val: T) : {
// assoc(__, val)(prop)
<K extends string>(prop: K): {
// assoc(__, val)(prop)(obj) when obj has key prop, tests if val is typeof obj[prop] for best return type
<U extends Record<K, any>>(obj: U): U[K] extends T ? U : Record<K, T> & Omit<U, K>;
// assoc(__, val)(prop)(obj) when obj does not have key prop
<U>(obj: U): U & Record<K, T>;
}

// assoc(__, val)(__, obj)
<U>(__2: Placeholder, obj: U): {
// assoc(__, val)(__, obj)(prop), prop is keyof obj, tests if val is typeof obj[prop] for best return type
Expand All @@ -26,14 +49,6 @@ export function assoc<T>(__: Placeholder, val: T) : {
<K extends keyof U, U>(prop: K, obj: U): U[K] extends T ? U : Record<K, T> & Omit<U, K>;
// assoc(__, val)(prop, obj), when obj does not have key prop
<K extends string, U>(prop: K, obj: U): U & Record<K, T>;

// assoc(__, val)(prop)
<K extends string>(prop: K): {
// assoc(__, val)(prop)(obj) when obj has key prop, tests if val is typeof obj[prop] for best return type
<U extends Record<K, any>>(obj: U): U[K] extends T ? U : Record<K, T> & Omit<U, K>;
// assoc(__, val)(prop)(obj) when obj does not have key prop
<U>(obj: U): U & Record<K, T>;
}
};

// assoc(prop, val)
Expand All @@ -44,30 +59,39 @@ export function assoc<T, K extends string>(prop: K, val: T) : {
<U>(obj: U): U & Record<K, T>;
};

// assoc(prop)
export function assoc<K extends string>(prop: K): {
// assoc(prop)(__, obj) when prop is keyof obj
<U extends Record<K, any>>(__: Placeholder, obj: U): {
// assoc(prop)(__, obj)(val) if val is typeof obj[prop]
// assoc(__, __, obj)
export function assoc<U>(__: Placeholder, __2: Placeholder, obj: U): {
// assoc(__, __, obj)(key)
<K extends keyof U>(key: K): {
// assoc(__, __, obj)(key)(value), when K is keyof U and value types match
<T extends U[K]>(val: T): U;
// assoc(prop)(__, obj)(val) if val is not typeof obj[prop]
<T>(val: T): Record<K, T> & Omit<U, K>;
// assoc(__, __, obj)(key)(value), when K is keyof U and value types do not match
<T>(val: T): Record<K, T> & Omit<U, K>
}
// assoc(prop)(__, obj) when prop is not keyof obj
<U>(__: Placeholder, obj: U): <T>(val: T) => U & Record<K, T>;

// assoc(prop)(val, obj) when obj has key prop, tests if val is typeof obj[prop] for best return type
<T, U extends Record<K, any>>(val: T, obj: U): U[K] extends T ? U : Record<K, T> & Omit<U, K>;
// assoc(prop)(val, obj) when obj does not have a key prop
<T, U>(val: T, obj: U): U & Record<K, T>;

// assoc(prop)(val)
<T>(val: T): {
// assoc(prop)(val)(obj) when obj has key prop and val is typeof obj[prop]
<U extends Record<K, T>>(obj: U): U;
// assoc(prop)(val)(obj) when obj has key prop and val is not typeof obj[prop]
<U extends Record<K, any>>(obj: U): Record<K, T> & Omit<U, K>;
// assoc(prop)(val)(obj) when obj does not have key prop
<U>(obj: U): U & Record<K, T>;
// assoc(__, __, obj)(key)(value), when K is not keyof U
<K extends string>(key: K): <T>(val: T) => U & Record<K, T>;
// assoc(__, __, obj)(__, value)
<T>(__: Placeholder, val: T): {
// assoc(__, __, obj)(__, value)(key), when obj has key prop, tests if val is typeof obj[prop] for best return type
<K extends keyof U>(key: K): U[K] extends T ? U : Record<K, T> & Omit<U, K>;
// assoc(__, __, obj)(__, value)(key) when obj does not have key prop
<K extends string>(key: K): U & Record<K, T>;
}
// assoc(__, __, obj)(key, value)
<K extends keyof U>(key: K, val: U[K]): U;
<K extends keyof U, T>(key: K, val: T): Record<K, T> & Omit<U, K>;
<K extends string, T>(key: K, val: T): U & Record<K, T>;
};

// 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<T, U>(__: Placeholder, val: T, obj: U): <K extends string>(prop: K) => K extends keyof U ? T extends U[K] ? U : Record<K, T> & Omit<U, K> : U & Record<K, T>;
// assoc(prop, __, obj)(val), when K is keyof obj, tests if val is typeof obj[prop] for best return type
export function assoc<U, K extends keyof U>(prop: K, __: Placeholder, obj: U): <T>(val: T) => T extends U[K] ? U : Record<K, T> & Omit<U, K>;
// assoc(prop, __, obj)(val), when prop is not keyof obj
export function assoc<U, K extends string>(prop: K, __: Placeholder, obj: U): <T>(val: T) => U & Record<K, T>;
// assoc(prop, val, obj) when prop is keyof obj and val is same type
export function assoc<K extends keyof U, U>(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<T, K extends keyof U, U>(prop: K, val: T, obj: U): Record<K, T> & Omit<U, K>;
// assoc(prop, val, obj) when prop is not keyof obj
export function assoc<T, U, K extends string>(prop: K, val: T, obj: U): U & Record<K, T>;

0 comments on commit 5cae602

Please sign in to comment.