build: let tsc find the bindings nothing reads

This project has no linter and does not want one, but `strict` says nothing
about a binding that is simply never read — and that is the one kind of dead
code a reader cannot tell apart from a deliberate seam. `tsc` already walks
every file, so it may as well answer the question.

Three had accumulated: an unread `type View` import in App, and a
`useGameMeta()` resolver in Controls and YourOwn, both of which draw their
colours from somewhere else — the game-order list has its own resolver, and
YourOwn reads the reader's own records directly. None of them were wrong, they
were just leftovers that read as intent.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Lucas Winther
2026-08-20 06:11:26 +02:00
co-authored by Claude Opus 5
parent 5ae559de97
commit 447ea25632
5 changed files with 11 additions and 5 deletions
+8 -1
View File
@@ -66,7 +66,14 @@ feed is a static JSON file built from snapshots, falling back to checked-in fixt
The only runtime dependency is `zod`. Do not add a bundler, test runner, HTTP client, or HTML
parsing library — Bun covers all four. `tsconfig.json` runs `strict` plus
`noUncheckedIndexedAccess` and `exactOptionalPropertyTypes`.
`noUncheckedIndexedAccess`, `exactOptionalPropertyTypes`, `noUnusedLocals` and
`noUnusedParameters`.
The last two are there because this project has no linter and does not want one: a binding nothing
reads is the only kind of dead code a reader cannot tell from a deliberate seam, and `tsc` already
walks every file. Three had accumulated behind `strict` — an unread `type View` import, and a
`useGameMeta()` resolver in two components that each draw their colours from somewhere else. A
genuinely unused parameter is spelled with a leading underscore, which both flags already exempt.
## Commands
+1 -1
View File
@@ -17,7 +17,7 @@ import { useAppUpdate } from "./state/useAppUpdate.ts";
import { useMarkSet } from "./state/useMarkSet.ts";
import { useProgress } from "./state/useProgress.ts";
import { useDailyLog, type DailyLogMap } from "./state/useDailyLog.ts";
import { adoptNewLanes, usePrefs, type View } from "./state/usePrefs.ts";
import { adoptNewLanes, usePrefs } from "./state/usePrefs.ts";
import { snapDayWidth } from "./state/zoom.ts";
import { useCustom } from "./state/useCustom.ts";
import { compareRows, SORT_MODES, type Activity, type SortMode } from "./state/sort.ts";
-1
View File
@@ -65,7 +65,6 @@ export function Controls({
/** Everything the reader entered themselves, and the ways to change it. */
own: React.ComponentProps<typeof YourOwn>;
}) {
const gameMeta = useGameMeta();
return (
<section className="border-t border-hairline px-4 py-5">
{/* Which games and how they are read on one side, what the reader has
-2
View File
@@ -1,6 +1,5 @@
import { useState } from "react";
import type { CustomEvents, CustomGames, LaneId } from "../../shared/custom.ts";
import { useGameMeta } from "../state/gameMeta.tsx";
import type { EventDraft } from "../state/useCustom.ts";
import { EventForm, GameForm } from "./CustomForms.tsx";
@@ -30,7 +29,6 @@ export function YourOwn({
onRemoveGame: (id: string) => { removed: boolean; blockedBy: number };
onAddEvent: (draft: EventDraft) => void;
}) {
const gameMeta = useGameMeta();
const [adding, setAdding] = useState<"game" | "event" | null>(null);
const [editing, setEditing] = useState<string | null>(null);
const [refusal, setRefusal] = useState<string | null>(null);
+2
View File
@@ -9,6 +9,8 @@
"noEmit": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitOverride": true,
"noFallthroughCasesInSwitch": true,
"exactOptionalPropertyTypes": true,