Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(assoc) overload reorder and unit tests #96

Merged
merged 2 commits into from
Feb 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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>;
Loading