I have a function that takes an object and set of keys that construct new type by picking by those keys.
I works fine when both parameters are defined in same function, but not when I try to make this function into function that takes only object parameter and returns function that takes in the keys.
I prepared test function that illustrate the issue:
const test = {
first: '',
second: '',
third: '',
};
export function fromObjectWithKeys<T extends { [key: string]: unknown }, K extends keyof T>(obj: T, ...keys: K[]) {
return (a: K) => a;
}
const res = fromObjectWithKeys(test, 'first', 'third')
//here type of res is: (a: ("first" | "third")) => ("first" | "third")
This one works as expected with second key not being present, next example however fails, losing information about filtered keys:
export function fromObject<T extends { [key: string]: unknown }, K extends keyof T>(obj: T) {
return (...keys: K[]) => {
return (a: K) => a;
};
}
const withKeys = fromObject(test);
const resFromKeys = withKeys('first', 'third');
// here resFromKeys type is: (a: ("first" | "third" | "second")) => ("first" | "third" | "second")
In second example information was lost when part of function was manually curried. How was type information lost? How can I fix it so I am able to have second function accept only one parameter while preserving types down in the hierarchy?
CodePudding user response:
Make the function that's returned generic, moving K
from the outer function to that function:
export function fromObject<T extends { [key: string]: unknown }>(obj: T) {
return <K extends keyof T>(...keys: K[]) => {
return (a: K) => a;
};
}
Then you get the same result as previously:
const withKeys = fromObject(test);
const resFromKeys = withKeys('first', 'third');
// here resFromKeys type is: (a: ("first" | "third")) => ("first" | "third")
(Thank you spender for pointing out that we could move K
rather than using a new generic!)