Model API Request States with TypeScript

Difficulty: medium

Model idle, loading, success, and error with a discriminated union. Validate unknown API input at runtime and use exhaustive handling.

Examples

Verify behavior

Input: The solution compiles with the supplied strict command.

Output: Remove @ts-expect-error: success without data must fail compilation.

Constraints

  • The solution compiles with the supplied strict command.
  • Remove @ts-expect-error: success without data must fail compilation.
  • Add a cancelled branch without updating render: exhaustiveness must fail.
  • parseName({name: "Ada"}) succeeds; null and {name: 3} return error.
Back to JavaScript practice

Guided TypeScript exercise · checked locally

Model API Request States with TypeScript

This workspace edits text only. Save your answer as lesson.ts in a project with TypeScript installed, then run the following command. No browser test result or completion is awarded here.

npx tsc --strict --noEmit --target ES2022 --skipLibCheck lesson.ts

Handle all four states, validate unknown API input, and include an invalid success state marked with @ts-expect-error. Remove that directive once to confirm the compiler rejects the invalid state; restore it for a passing check.

Reference solution and walkthrough
type RequestState<T> = { status: 'idle' } | { status: 'loading' } | { status: 'success'; data: T } | { status: 'error'; error: Error };
const unreachable = (value: never): never => { throw new Error(`Unexpected state: ${String(value)}`); };
export function render(state: RequestState<string>) { switch (state.status) { case 'idle': return 'Start a request'; case 'loading': return 'Loading'; case 'success': return state.data; case 'error': return state.error.message; default: return unreachable(state); } }
export function parseName(input: unknown): RequestState<{ name: string }> { if (typeof input === 'object' && input !== null && 'name' in input && typeof input.name === 'string') return { status: 'success', data: { name: input.name } }; return { status: 'error', error: new Error('Invalid API payload') }; }
// @ts-expect-error success requires data
const invalid: RequestState<string> = { status: 'success' };

Walkthrough

  1. This is a guided compiler exercise, not a browser JavaScript test. Save your implementation as lesson.ts in a project with TypeScript installed.
  2. Use a union discriminated by status. Success requires data and error requires Error; idle and loading require neither. Switch on status to narrow the available fields.
  3. Handle every branch and pass the default value to a never function. Adding a new status then produces a compile error until the renderer handles it.
  4. Validate unknown JSON before building a success state. Type annotations disappear at runtime; the parseName guard checks the object and name property.
  5. Run npx tsc --strict --noEmit --target ES2022 --skipLibCheck lesson.ts. The reference solution must pass with @ts-expect-error present and fail when that directive is removed.

Acceptance checklist

  1. The solution compiles with the supplied strict command.
  2. Remove @ts-expect-error: success without data must fail compilation.
  3. Add a cancelled branch without updating render: exhaustiveness must fail.
  4. parseName({name: "Ada"}) succeeds; null and {name: 3} return error.

Interview follow-ups

  1. Why is a type assertion not JSON validation?
  2. When would you use a schema validator?

Reference: https://www.typescriptlang.org/docs/handbook/2/narrowing.html (accessed 2026-09-07).

More JS Coding

View all JS Coding →
JS CodingMedium

5. Throttle

JS CodingMedium

13. Deep Clone

JS CodingMedium

14. Event Emitter

JS CodingEasy

66. Promise Methods

JS CodingEasy

19. Memoize I

JS CodingMedium

86. API Retry

Related hubs

  • All JavaScript coding
  • JavaScript interview questions
  • JS polyfill interview questions
  • Debounce & throttle questions
  • React interview hub