Debugging challengeIntermediate
The comparator that silenced the compiler
Scenario
A leaderboard library exposes sortBy(items, key) and maxBy(items, key), generic over any object type. Sorting players by score works perfectly and is covered by tests. Sorting by name returns the players in their original insertion order — no error, no warning — and maxBy(players, 'name') always returns whichever player happens to be first. The code compiles clean under strict: true. Find the bug, and explain why the compiler *did* try to prevent it.
1interface Player { name: string; score: number; rank: number }2 3function sortBy<T, K extends keyof T>(items: T[], key: K): T[] {4 // First attempt was `a[key] - b[key]`, which tsc rejected:5 // error TS2362: The left-hand side of an arithmetic operation must be of6 // type 'any', 'number', 'bigint' or an enum type.7 // "Fixed" by coercing both sides:8 return [...items].sort((a, b) => Number(a[key]) - Number(b[key]));9}10 11function maxBy<T, K extends keyof T>(items: T[], key: K): T | undefined {12 let best: T | undefined;13 for (const it of items) {14 if (best === undefined || Number(it[key]) > Number(best[key])) best = it;15 }16 return best;17}18 19const players: Player[] = [20 { name: 'zoe', score: 12, rank: 3 },21 { name: 'amir', score: 98, rank: 1 },22 { name: 'lena', score: 55, rank: 2 },23];24 25console.log(sortBy(players, 'score').map((p) => p.name)); // ['zoe', 'lena', 'amir'] — works26console.log(sortBy(players, 'name').map((p) => p.name)); // ['zoe', 'amir', 'lena'] — untouched27console.log(maxBy(players, 'name')?.name); // 'zoe' — looks right by luck…28console.log(maxBy(players.slice(1), 'name')?.name); // 'amir' — expected 'lena': always the first elementYour task
- What does
Number('amir')evaluate to, and what does the comparator return when both operands are player names? What does the sort algorithm do with that return value? - The original
a[key] - b[key]was a compile error. What was TS2362 actually telling the author aboutT[K], and why did wrapping both sides inNumber(...)make the error disappear without making the code correct? - Explain why
maxBy(players, 'name')returns the *first* element rather than a random one. - Rewrite the API with a selector function
(item: T) => numberso the bad call fails to compile. Which call sites break, and why is that the point? - Keep a key-based API too: write a
NumericKeys<T>conditional type so thatsortBy(players, 'name')is rejected at compile time whilesortBy(players, 'score')still works. - State the complexity and whether the fixed sort is stable.
DebuggingSystematic ReasoningImplementation
Work it out
Write your analysis before revealing anything. The self-check below compares it against what a strong answer contains.
Reveal
Progressive — each section builds on the previous one.
The bug
Why it happens
The fix
Edge cases
Complexity
Self-check
Tick what your analysis covered. Be honest — this feeds your readiness profile.