TypeDrop
2026-09-26 Challenge
2026-09-26
Easy
Typed Expense Splitter
You're building the core logic for a group expense-splitting app (think Splitwise). Friends log shared expenses, and the app must calculate how much each person owes or is owed — producing a strongly-typed settlement summary the compiler fully understands.
Goals
- Implement `computeBalances` using discriminated-union narrowing on `SplitRule.kind` to compute each participant's net balance in cents.
- Implement `minimiseSettlements` with a greedy creditor/debtor matching algorithm that produces the minimum number of transfers.
- Implement `splitExpenses` to compose the two functions above and return an explicitly-typed `SplitSummary`.
- Implement `formatSettlement` with a template literal return type that formats cent amounts into a `$dollars.cents` string.
challenge.ts
// Key domain types
type Participant = string;
type SplitRule =
| { kind: "equal" }
| { kind: "shares"; participants: Record<Participant, number> }
| { kind: "exact"; participants: Record<Participant, number> };
interface Expense {
id: string;
description: string;
paidBy: Participant;
amountCents: number; // integer cents, > 0
splitRule: SplitRule;
}
type Balances = Record<Participant, number>; // + owed, - owes
interface Settlement { from: Participant; to: Participant; amountCents: number; }
interface SplitSummary { balances: Balances; settlements: Settlement[]; settled: boolean; }
// Functions you must implement:
function computeBalances(expenses: Expense[], participants: Participant[]): Balances { … }
function minimiseSettlements(balances: Balances): Settlement[] { … }
function splitExpenses(expenses: Expense[], participants: Participant[]): SplitSummary { … }
function formatSettlement(s: Settlement): `${string} owes ${string} $${string}` { … }
Hints (click to reveal)
Hints
- When narrowing `SplitRule`, a `switch (rule.kind)` block gives TypeScript full discriminated-union narrowing — no type assertions needed.
- For the template literal return type, derive dollars with `Math.floor(amountCents / 100)` and cents with `(amountCents % 100).toString().padStart(2, '0')`.
- In `minimiseSettlements`, sort creditors and debtors by absolute balance descending before the greedy loop so the largest transfers are matched first.
Useful resources
Or clone locally
git clone -b challenge/2026-09-26 https://github.com/niltonheck/typedrop.git