I want to make a function like
function sortByKey<T>(items: T[], key: string): T[] {
return items.sort((a, b) => a[key] - b[key]);
}
I need T[key] to be a number, but I'm not sure how to express that.
If I knew the key ahead of time I could obviously just do {key: number} but that doesn't work here.
I tried something like sortByKey<K>(items: {[k: K]: number}[], key: K) but that gives the error "An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead." I looked into mapped types but they don't seem to do what I need. Is something like this possible in TypeScript?
CodePudding user response:
You probably want the function to be generic both in T, the type of the items elements, and in K, the type of key. You can constrain K to be keylike (K extends PropertyKey) and constrain T to be a type with a number value at key K (T extends Record<K, number> using the Record<K, V> utility type):
function sortByKey<K extends PropertyKey, T extends Record<K, number>>(
items: T[],
key: K
): T[] {
return items.sort((a, b) => a[key] - b[key]);
}
You can't write {[k: K]: number} because that's an index signature which can't be generic. But you can write {[P in K]: number} using a mapped type. Mapped types are similar to but distinct from index signatures; see this answer for more information. Anyway, Record<K, number> is an alias for {[P in K]: number}, so you were getting close.
