'How do I enforce a key of an object to be of a certain type?
I'm trying to make a function that can take a generic objects key and use that to create a function to sort string properties using String.localCompare but I can't get TypeScript to understand that a[key] and b[key] are supposed to be strings.
How do I enforce this in TS?
const makeStringSort = <T>(key: keyof T) => {
return (a: T, b: T) => {
return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
};
};
Property 'toLowerCase' does not exist on type 'T[keyof T]'
// example usage
interface Item {
prop: string;
other: number;
}
const sortFn = makeStringSort<Item>("prop");
const items: Item[] = [];
items.sort(sortFn);
Solution 1:[1]
Updated answer: use mapped types
const makeStringSort = <T extends string>(key: T) => {
return <
U extends {
[Property in T]: string;
}
>(
a: U,
b: U
) => {
return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
};
};
Solution 2:[2]
Reading your code seems that key is a subtype of string and a and b are Record<string, string>.
const makeStringSort = <T extends Record<string, string>>(key: keyof T) => {
return (a: T, b: T) => {
return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
};
};
Solution 3:[3]
If you define a helper:
type KeysOfType<O, T> = {
[K in keyof O]: O[K] extends T ? K : never;
}[keyof O];
to extract the keys of O that have a value of type T, then you can use it to constrain your function:
const makeStringSort = <T>(key: KeysOfType<T, string>) => {
return <TV extends { [K in KeysOfType<T, string>]: string }>(a: TV, b: TV) => {
return a[key].toLowerCase().localeCompare(b[key].toLowerCase());
};
};
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Mirco Bellagamba |
| Solution 3 |
