TypeScript Intermediate: Utility Types, Narrowing & Advanced Types

Lesson 7 of 7

Final Project: Build a Typed Settings Validator

Combine utility types, narrowing, mapped/template literal types, and satisfies into one small but real module: a settings validator.

Requirements

  1. Define a Settings interface with at least four fields of mixed types (e.g. theme: 'light' | 'dark', fontSize: number, autoSave: boolean, username: string).
  2. Write an isSettings(value: unknown): value is Settings type predicate that validates an untrusted value (e.g. something parsed from JSON.parse) before it's ever treated as Settings.
  3. Write an updateSettings(current: Settings, changes: Partial<Settings>): Settings function, changes should be optional and partial, not a full replacement.
  4. Use a mapped type to derive a SettingsChanged type, { [K in keyof Settings]: boolean }, and a function that compares two Settings objects and returns one, flagging exactly which fields differ.
  5. Build a defaultSettings object typed with satisfies Settings, so each default value keeps its own precise type instead of being widened to a single union.

Stretch goals

  • Add a template literal type deriving an event name per settings key, e.g. on + the capitalized key + Change, and a tiny typed event emitter that only accepts those names.
  • Use Pick/Omit to derive a "public settings" type that hides one internal-only field from an exported API.
  • Add a resetSettings(): Settings that returns a fresh copy of defaultSettings, and verify at the type level that defaultSettings itself can't be mutated by accident.

Submit your repository link below when you are done, an instructor will review it before you can mark this lesson complete. Good luck! 🚀

This lesson ends in a project. Build it on your own machine, there's nowhere to submit it here, but the brief above is everything you need.