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