TypeDrop
2026-06-29 Challenge
2026-06-29
Medium
Typed Feature Flag Engine with Targeting Rules & Rollout Segments
You're building the feature-flag evaluation engine for a SaaS platform. Raw flag configurations arrive as `unknown` from a remote config store; your engine must validate them into strongly-typed flag definitions, evaluate targeting rules against a typed user context, and return a fully-typed evaluation result — with zero `any`.
Goals
- Complete the `FlagValue<T>` conditional type so each `FlagValueType` string literal maps to its corresponding TypeScript primitive.
- Implement `validateFlagDefinition` to parse an `unknown` blob into a `FlagDefinition<FlagValueType>`, collecting all `ValidationError`s and returning a `Result<FlagDefinition<FlagValueType>, ValidationError[]>`.
- Implement `evaluateRule` using an exhaustive type-narrowing `switch` on `rule.kind`, and `evaluateFlag` to walk the rule list and return the correct `EvalResult`.
- Complete `FlagRegistryBuilder` so `.add()` widens the generic key union and `.build()` returns a correctly-typed `FlagRegistry<K>`.
challenge.ts
// Key types and main function signature preview
type FlagValueType = "boolean" | "string" | "number";
type FlagValue<T extends FlagValueType> =
T extends "boolean" ? boolean :
T extends "string" ? string :
T extends "number" ? number : never;
type TargetingRule =
| { kind: "exact"; attribute: string; value: string }
| { kind: "set"; attribute: string; values: readonly string[] }
| { kind: "rollout"; percentage: number };
interface FlagDefinition<T extends FlagValueType> {
readonly id: string;
readonly valueType: T;
readonly rules: ReadonlyArray<{ rule: TargetingRule; value: FlagValue<T> }>;
readonly defaultValue: FlagValue<T>;
}
// Evaluate a single flag for a user — returns a typed discriminated result
declare function evaluateFlag<K extends string>(
registry: FlagRegistry<K>,
flagId: string,
ctx: UserContext
): EvalResult<FlagValueType>;
Hints (click to reveal)
Hints
- For `FlagValue<T>`, chain `T extends 'boolean' ? boolean : T extends 'string' ? string : ...` — conditional types distribute over each branch.
- In `FlagRegistryBuilder.add()`, the return type `FlagRegistryBuilder<K | typeof flag.id>` is already provided — store flags in a private `Map<string, FlagDefinition<FlagValueType>>` and cast the builder's `this` on return.
- In `evaluateRule`'s `switch`, add a default branch that assigns `rule` to `never` (the exhaustiveness sentinel) — TypeScript will error if you forget a case.
Useful resources
Or clone locally
git clone -b challenge/2026-06-29 https://github.com/niltonheck/typedrop.git