The compiler and runtime

Build a typed task list

Combine object types, literal unions, functions, narrowing, generics, and runtime parsing in one small program.

Build a command-line task list that reads and writes a JSON file.

Start with a state model that prevents impossible combinations:

type TaskStatus = 'open' | 'completed'

type Task = {
  id: number
  title: string
  status: TaskStatus
}

Support four commands: add, list, complete, and remove. Give every function a clear input contract. Let local return types infer unless an exported boundary needs an explicit promise.

Treat the JSON file as unknown. Validate the top-level array and every task field before returning Task[]. A type assertion does not count as parsing.

Add this helper only if it preserves useful information:

function findById<T extends { id: number }>(items: T[], id: number) {
  return items.find(item => item.id === id)
}

The result keeps the caller’s specific item type and includes undefined when no item matches.

Test these failure paths:

  • the file does not exist
  • the JSON is malformed
  • one task has a string ID
  • an unknown command is passed
  • a requested task does not exist

Keep strict enabled and add a repeatable check:

npx tsc --noEmit

Finish by removing any any, unjustified assertion, or non-null assertion. The goal is not to make the checker quiet. The goal is to make every uncertain boundary and missing value explicit.

Lesson completed

Take this course offline

Get every free book, course edition, and software download.

Get the download library →