Coding problem · Easy · Language Idioms
Type-safe groupBy<T, K>()
Easy Language practicals Seen at Microsoft, Shopify, Vercel
Implement a generic groupBy with this signature:
function groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: (item: T) => K): Record<K, T[]>
It groups items by the key returned from keyFn, preserving input order within each group. Keys appear in the result in the order they were first seen.
(Types are erased at runtime, so the tests check behaviour; reviewers check that your types compile under strict.)
Hints
Hint 1
PropertyKey is string | number | symbol — exactly what object keys can be.
Hint 2
Start with {} as Record<K, T[]> and push into (acc[key] ??= []).
Write and run your solution
Loading the editor… (JavaScript is required to run code in your browser.)
Solution and explanation
Show the solution
Time O(n) · Space O(n)
The generic parameters let TypeScript infer T from the array and K from the callback's return type, so groupBy(people, p => p.team) is typed as Record<string, Person[]> (or a union of literal keys if team is a union). K extends PropertyKey restricts keys to what objects can hold. ??= creates each bucket lazily.
Modern runtimes ship Object.groupBy (ES2024) and Map.groupBy, which return a null-prototype object / a Map — worth mentioning, together with why a Map is safer for arbitrary keys like "__proto__".
Reference solution
function groupBy<T, K extends PropertyKey>(items: readonly T[], keyFn: (item: T) => K): Record<K, T[]> {
const groups = {} as Record<K, T[]>;
for (const item of items) {
(groups[keyFn(item)] ??= []).push(item);
}
return groups;
}
Common mistakes
- Typing the result as
anyor{[key: string]: any}and losing inference. - Using a plain object with untrusted keys (
__proto__) — preferMapor a null-prototype object.
Follow-up questions
- Return a
Map<K, T[]>instead. - Write
countByandindexBywith the same generics.