TypeDrop
2026-05-25 Challenge
2026-05-25
Easy
Typed User Permission Checker
You're building the access-control layer for a SaaS dashboard. Raw user session data arrives as `unknown` from a JWT-decode utility; your engine must validate it, derive a typed permission set from the user's role, and answer permission queries — with zero `any`.
Goals
- Define `PermissionMap` as a mapped type over `Role` and populate `PERMISSIONS` using the `satisfies` operator.
- Implement `parseSession` to validate `unknown` input into a typed `UserSession` via a discriminated-union `ParseResult` — no `any` or type assertions.
- Write a `isRole` type-guard that narrows `unknown` to the `Role` union without casting.
- Implement `can` and `checkAll` to answer permission queries against the typed permission table.
challenge.ts
/** Every discrete action a user might attempt. */
type Action =
| "read:content" | "write:content" | "delete:content"
| "manage:users" | "view:analytics" | "export:data";
type Role = "admin" | "editor" | "viewer";
/** Mapped type: every Role key is guaranteed present */
type PermissionMap = { readonly [R in Role]: ReadonlyArray<Action> };
/** Discriminated-union result from the session parser */
type ParseResult =
| { ok: true; session: UserSession }
| { ok: false; reason: string };
// --- functions you must implement ---
declare function parseSession(raw: unknown): ParseResult;
declare function can(session: UserSession, action: Action): boolean;
declare function checkAll(
session: UserSession,
actions: ReadonlyArray<Action>
): Partial<Record<Action, boolean>>;
Hints (click to reveal)
Hints
- A type-guard `isRole(v: unknown): v is Role` lets the compiler accept `rec['role']` as `Role` inside `parseSession` without any cast.
- The `satisfies` operator validates an object's shape against a type while preserving its narrower literal types — perfect for `PERMISSIONS`.
- `Object.fromEntries(actions.map(a => [a, can(session, a)]))` produces the batch report in one expression; you only need a light cast at the boundary.
Useful resources
Or clone locally
git clone -b challenge/2026-05-25 https://github.com/niltonheck/typedrop.git