Code

src/state.ts — Readonly state

In the code column — src/state.ts. It's lesson 9's pub/sub, now typed — with one crucial guard.

getState() returns Readonly<AppState>:

export function getState(): Readonly<AppState> {
  return state;
}

Readonly<T> makes every field read-only. A view can read getState().pictures, but:

getState().pictures = [];          // ❌ Cannot assign to 'pictures' because it is read-only

doesn't compile. This is the language enforcing lesson 9's convention: state can only be changed through actions (setPictures, setStatus, setSort) that end in notify(). In the vanilla version that was just discipline; now it's a compiler rule.

catch (error) is unknown — the third idea, in errors.ts:

export function toMessage(error: unknown): string {   // unknown, NOT any
  if (error instanceof AppError) return error.message;
  if (error instanceof Error) return error.message;
  return 'Something went wrong.';
}

Why unknown? Because JavaScript lets you throw anything: throw 42, throw 'oops', throw null. So in a catch block TypeScript types the error as unknown and forces you to narrow (instanceof) before using it. With any you could write error.message on a number and crash at runtime.

Verify.

getState().searchQuery = 'x';      // ❌ read-only
setSearchQuery('x');               // ✅ through an action

// state.ts getFilteredSaved: switch (state.sortKey) with no `default` —
// add a fourth SortKey value, and the switch stops compiling.

Tip. Readonly<AppState>'s read-only-ness is shallow (top-level fields only). For deeply nested objects there are DeepReadonly patterns, but shallow is usually enough: it catches the most common mistake — a view trying to overwrite state directly instead of using an action.