Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

TS – lost type information on filtered keys in curried function

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:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

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?

>Solution :

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")

Playground link

(Thank you spender for pointing out that we could move K rather than using a new generic!)

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading