Files
redefined-designs/backend/src/types.ts
T
bermudalamb d43e2d5871
Linting / lint (pull_request) Successful in 2m11s
SonarQube Analysis / sonarqube (pull_request) Failing after 5m2s
refactor(backend): type the admin item queries, and fix the stale status union (#159)
admin.ts has no untyped reads left. Typed sites go from 43 to 49.

New ItemRecord in itemSelect.ts for the bare `items` row that `RETURNING *` gives back. Deliberately not AdminItemRow: that describes a select which joins the category and adds images and tags as subqueries, so typing a RETURNING * as it would promise three fields the result does not contain. Three shapes for one table, because three different queries return three different things.

The typing found a real defect on its first run, which is the case for doing this at all.

`ItemStatus` in types.ts was `'available' | 'reserved' | 'sold'`. The database has four values and defaults to 'pending' — items have arrived pending since #90. itemFilters.ts declared its own copy that had all four and was correct. Two declarations of one union with nothing connecting them: one went stale and nothing said so.

It was invisible while query rows were `any`. Typing them turned `if (status === 'pending')` in admin.ts into TS2367, "this comparison appears to be unintentional because the types 'ItemStatus' and '\"pending\"' have no overlap" — a compiler telling us the unpublish route's guard could never be true, against a type that was simply wrong.

Confirmed against the database rather than by picking the more plausible of the two declarations: `SELECT DISTINCT status FROM items` returns pending, available, reserved and sold.

Fixed by removing the duplication rather than by patching both copies. types.ts now holds the only declaration and itemFilters.ts imports it, re-exporting so its existing importers are unaffected. Patching both would have left the next drift free to happen the same way.

Verified: tsc clean, unit 254/254, integration 238/238, and backend lint unchanged — the four warnings it reports are identical to those on main with these changes stashed, so none of them are new.

Refs #159
2026-08-24 14:15:27 -05:00

39 lines
1.1 KiB
TypeScript
Executable File

/**
* Every value `items.status` can hold.
*
* 'pending' was missing here from the moment items started arriving pending,
* while itemFilters.ts declared its own copy that had it. Two declarations of
* one union is how that happens: nothing connects them, so one goes stale and
* nothing says so. The stale one was harmless only because query rows were
* `any` — typing them turned `status === 'pending'` in admin.ts into a compile
* error about a comparison with no overlap, which is how it was found.
*
* This is now the single declaration. itemFilters.ts imports it.
*/
export type ItemStatus = 'pending' | 'available' | 'reserved' | 'sold';
export interface ItemImage {
id: number;
image_path: string;
sort_order: number;
}
export interface ItemTag {
id: number;
name: string;
color: string;
}
export interface Item {
id: number;
name: string;
description: string | null;
price_cents: number;
images: ItemImage[];
status: ItemStatus;
reserved_until: string | null;
sold_at: string | null;
paypal_order_id: string | null;
created_at: string;
}