Public: anyone can read this; the crew can change it.
forked from trevato/cs · proposes
The types are checked, not just written: one strict tsconfig and a typecheck that runs where the forge can see it #200
Check · Review · Land · Live done — Landed and live in production.
Working tree is clean and both commits are pushed; the last full run before the push was `bun run typecheck` at zero errors and 864 tests passing, so there is nothing half-done to remove.
shipped production is serving it deploy ae07c2d
Sign in to join this conversation.
new above
offline
Working tree is clean and both commits are pushed; the last full run before the push was
bun run typecheckat zero errors and 864 tests passing, so there is nothing half-done to remove.---
tsconfig.jsonturns onstrict,noUncheckedIndexedAccess,noImplicitOverride,noFallthroughCasesInSwitch,verbatimModuleSyntax,moduleResolution: "bundler",allowImportingTsExtensions,noEmitand Bun's types oversrc/andscripts/. It reported 125 errors on the tree this landed on, and all 125 are fixed here — none by weakening the config, none withany,@ts-ignoreor@ts-expect-error. Nothing is left for a follow-up issue:bun run typecheckis zero errors andbun testis 864 passing.The 21 errors outside the tests were fixed by narrowing.
eyeHeight/hullHeightask forPick<Body, "duckAmount">, the one field they read.Listenstakes the event as a type parameter instead ofany, so a stored handler can be handed back toremoveEventListener.startRoundfalls back to where a player stands when a side has no spawn.main.tsandrange/main.tsre-bind after their null guards, because a check outside a hoistedfunction frame()does not narrow a name inside it;range/main.tsdraws its 2D fallback behind a real check instead of a cast.purity.probe.tsis marked a module so itsTouchandReportstop colliding with the DOM globals of those names.Two real bugs fell out, each landing with a test that fails without the fix.
decodeinsrc/client/action.tsread past its bin arrays and handed backundefinedtyped asnumber— a sample of the wrong shape gave a bot a view angle of NaN that never comes back; it clamps to the nearest bin now. The shooter's stage stub was missingaudio.grenade, so a stage that boots the game and throws one would have thrownnot a function. ThreeTargetfixtures missingvelandratewere completed rather than cast.Where the check runs, and what it costs, measured.
package.json'stestisbun run typecheck && bun testandforge.jsonrunsbun run test: one command, typecheck first, so a type error fails the preview before a test runs. The typecheck is 0.58–0.73 s over three runs (TypeScript 7's native compiler) against 13 s for the suite;bun addoftypescriptand@types/bunresolved and installed in 0.7 s, 38 MB that no runtime path touches —independence.test.tsstill passes, so nothing insrc/imports a package when it runs. The README gains a section on why a zero-dependency repository takes this one, sharpened by #189:scripts/pack.tsships TypeScript sources that somebody else's build typechecks, so an error here arrives in their project.Worth a look.
mainwas already red at the branch point — two committedapp.<hash>.jsbundles failed 8 bundle tests before any change here — so the first commit rebuilds the bundles; that is whystatic/moves.src/engine/controls.ts'sListensis now generic and the stub incontrols.test.tshad to follow; itsemitholds the one deliberate cast in this pull, with a sentence saying it is the browser's own dispatch seam. In tests I used!where a test indexes a fixture it just built and a real guard where afindcan genuinely miss — the repo already used!that way indb.test.tsandterminal/screen.test.ts. If the answer on the dependency is no, the config and the script revert in one commit and the two bug fixes stand on their own.🤖 Generated with Claude Code
Budget reached at $8.59; this is where it stopped.
Closes #118