TypeDrop

2026-10-21 Challenge

2026-10-21 Medium

Typed In-Memory Query Engine

You're building a lightweight client-side query engine for a product catalog dashboard. It must filter, sort, and aggregate a typed collection of records — and the compiler must guarantee that every field name, comparator, and aggregation function is valid for the shape of the record being queried.

Goals

  • Implement `KeysOfType<T, V>` to extract only the keys of T whose value extends V, and use it to restrict `AggregateClause` to numeric fields.
  • Implement `applyFilter` to evaluate all six comparison operators plus case-insensitive `contains`, using `typeof` guards to handle mixed Primitive types safely.
  • Implement `runQuery` to apply AND-chained filters, then sort, then limit — computing aggregations over the full filtered set before the limit is applied.
  • Ensure `rows` is always `[]` when aggregations are requested, and that `totalMatched` always reflects the pre-limit filtered count.
challenge.ts

// Key types you'll work with:
type Primitive = string | number | boolean;
type FlatRecord = Record<string, Primitive>;

// Links field name to its value type — no mismatched comparisons!
type FilterClause<T extends FlatRecord> = {
  [K in keyof T]: { field: K; op: CompareOp; value: T[K] };
}[keyof T];

// Only numeric fields are legal aggregation targets
type AggregateClause<T extends FlatRecord> = {
  op: AggregateOp;
  field: KeysOfType<T, number>; // ← compiler rejects string/boolean fields
  alias: string;
};

// Main entry point
function runQuery<T extends FlatRecord>(
  data: T[],
  query: Query<T>
): QueryResult<T> { /* TODO */ }
Hints (click to reveal)

Hints

  • For `FilterClause<T>`, map over `keyof T` to create a per-key object, then index with `[keyof T]` to turn it into a union — this is the standard trick for pairing correlated fields.
  • In `applyFilter`, use `typeof record[clause.field]` guards before applying `>` / `<` so TypeScript (and your runtime logic) handles booleans correctly.
  • Compute aggregations before slicing with `limit` — store the full filtered array, run aggregations on it, then apply `slice(0, limit)` for `rows`.

Or clone locally

git clone -b challenge/2026-10-21 https://github.com/niltonheck/typedrop.git