TypeDrop
2026-08-20 Challenge
2026-08-20
Easy
Typed groupBy & Aggregation with Mapped Types
You're building the analytics dashboard for a small e-commerce platform. Sales records stream in from the backend and you need to group them by a chosen key, then compute per-group summaries — all without losing type information about which keys are valid grouping fields.
Goals
- Define `GroupableKey` using a conditional mapped type that automatically filters SaleRecord keys whose values are strings.
- Define `GroupedSummary<K>` as a mapped/Record type whose keys are exactly the value union of `SaleRecord[K]`.
- Implement `groupAndSummarise` generically so it groups records, computes per-group summaries, and pre-populates zero entries for every possible group value.
- Implement `topGroup` returning the highest-`totalAmount` group key typed as `SaleRecord[K]`, not `string`.
challenge.ts
export type Category = "electronics" | "clothing" | "books" | "home";
export type Region = "north" | "south" | "east" | "west";
export interface SaleRecord {
id: string;
category: Category;
region: Region;
amount: number;
quantity: number;
}
export interface GroupSummary {
totalAmount: number;
totalQuantity: number;
count: number;
avgAmount: number;
}
// Keys of SaleRecord whose value is a string — derived via conditional type
export type GroupableKey = { [K in keyof SaleRecord]: SaleRecord[K] extends string ? K : never }[keyof SaleRecord];
// Mapped type: GroupedSummary<"category"> ≡ Record<Category, GroupSummary>
export type GroupedSummary<K extends GroupableKey> = Record<SaleRecord[K], GroupSummary>;
export function groupAndSummarise<K extends GroupableKey>(
records: SaleRecord[],
key: K,
allValues: ReadonlyArray<SaleRecord[K]>,
): GroupedSummary<K> { throw new Error("Not implemented"); }
export function topGroup<K extends GroupableKey>(
summary: GroupedSummary<K>,
): SaleRecord[K] { throw new Error("Not implemented"); }
Hints (click to reveal)
Hints
- For `GroupableKey`, map over `keyof SaleRecord`, emit `K` when `SaleRecord[K] extends string`, emit `never` otherwise, then index the result with `[keyof SaleRecord]`.
- For `GroupedSummary<K>`, think of it as `Record<SaleRecord[K], GroupSummary>` — the key of the Record is an indexed-access type.
- In `topGroup`, `Object.entries` returns `[string, GroupSummary][]`; you'll need one cast at the return site from `string` to `SaleRecord[K]` — this is the one place a cast is acceptable since the keys come from the Record itself.
Useful resources
Or clone locally
git clone -b challenge/2026-08-20 https://github.com/niltonheck/typedrop.git