feedback5 R6: add a small checkJs slice and core typedefs

tsc checks shared (except protocol.js and bandBonds.js), the sim rng/dir/constants helpers, and types/core.js. strict stays off. types/README.md says how to widen the slice.
This commit is contained in:
sganggs committed 2026-10-05 16:18:28 +08:00
1 parent babc35b72a
commit 64a7f48efa
5 files changed
+193 -3

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
# Typecheck
`npm run typecheck` runs `tsc --noEmit --checkJs -p jsconfig.json`.
The first slice is the closed set that already typechecks:
- `shared/` except `shared/protocol.js` and `shared/bandBonds.js` (their JSDoc does not match the code yet; see below)
- `server/sim/rng.js`, `server/sim/dir.js`, `server/sim/constants.js` (the last imports `GEO` from shared)
- `types/` — JSDoc for `Unit`, `DamageInfo`, the snapshot tuples, and the protocol message envelope
`strict` is off. `tsc` does not read `jsconfig.json` on its own; the script passes `-p`.
## Widen it
Add one file or one folder to `jsconfig.json` `include`, then run `npm run typecheck`. Fix JSDoc that disagrees with the code in that folder. Leave `strict` off until the folder reports nothing. Do not add `@ts-nocheck`.
Suggested order once the sim split settles: `server/sim/grid.js`, `server/sim/body.js`, `server/sim/buffs.js`, then `server/sim/damage.js` against the typedefs in `types/core.js`. The sim does not import `types/`. The typedefs are a reference, not a runtime dependency.
`types/core.js` follows docs/DESIGN.md §5.2, §5.4, §8 and docs/SIM.md §4, §9. Where those two disagree on `DamageInfo.type`, the typedef uses the union `makeDamageInfo` actually builds (`'elemental'` as well as `'element'`).
## Not in the first slice
`shared/protocol.js` — the catalogue JSDoc is ahead of a few object literals (`$optional` sits on a `Record` of checkers; `unitStatsEntry` does not list `side` or `silenced`, and the code reads `side`). Fix the comments, then remove it from `exclude`.
`shared/bandBonds.js` — the data-shape JSDoc rejects an empty fallback object and a tuple callback. Same treatment.
+119
View File
@@ -0,0 +1,119 @@
/**
* Core shapes for the sim and the wire protocol.
*
* Taken from docs/DESIGN.md §5.2 (Unit), §5.4 (DamageInfo), §8 (envelope, b.snap)
* and docs/SIM.md §4 (DamageInfo as makeDamageInfo builds it) and §9 (snapshot tuples).
* The sim does not import this file. It is here so the shapes have one JSDoc home
* while server/sim is still being split.
*/
/**
* A unit inside one battle. Minimum fields from DESIGN §5.2.
* Stats (`maxHp`, `atk`, …) are recomputed by the sim and are not all listed here.
*
* @typedef {Object} Unit
* @property {number} id unique per battle
* @property {'ally'|'enemy'} side
* @property {'op'|'token'|'enemy'|'device'} kind
* @property {string} defId
* @property {string|number} ownerId player id; enemies use the field owner or the source player
* @property {number} x
* @property {number} y
* @property {number} [tileR] operators
* @property {number} [tileC] operators
* @property {'UP'|'RIGHT'|'DOWN'|'LEFT'} [dir] allies: deploy direction. Enemies are not meaningful here.
* @property {number} facing horizontal sign, +1 or −1 (LEFT is −1)
* @property {number} hp
* @property {boolean} alive
* @property {number} sp
* @property {Object[]} [buffs]
* @property {number} [statusFlags]
* @property {number[]} [blocking] operators: ids of enemies they block
* @property {number|null} [blockedBy] enemies: blocker id
* @property {number[][]} [liveRangeGrid] the attack range the detail card shows
* @property {string} [form] model form (enemy mode, or a doll's `'doll'`)
*/
/**
* One damage instance, after `makeDamageInfo` (server/sim/damage.js).
* DESIGN §5.4 lists `type` as `'phys'|'arts'|'true'|'element'`.
* SIM §4 and the normaliser also use `'elemental'` (HP damage from an element burst)
* and keep `'element'` for gauge fill, which removes no HP.
*
* @typedef {Object} DamageInfo
* @property {number} amount pre-mitigation
* @property {'phys'|'arts'|'true'|'elemental'|'element'} type
* @property {'burn'|'necrosis'|'neural'|'apoptosis'|string|null} [element]
* @property {number} [atkScale]
* @property {number} [defIgnoreFlat]
* @property {number} [defIgnorePct]
* @property {number} [resIgnoreFlat]
* @property {number} [resIgnorePct]
* @property {number} [mul]
* @property {boolean} [canDodge]
* @property {boolean} [isSkill]
* @property {boolean} [isSplash]
* @property {boolean} [isAttack]
* @property {boolean} [isProjectile]
* @property {string[]} [tags]
* @property {boolean} [cancel] set by a `hit` hook to drop the instance
* @property {boolean} [noSp]
* @property {boolean} [ignoreSleep]
* @property {boolean} [ignoreSelect] reaches an airborne ally; the hit selected nobody
* @property {boolean} [sourceless] 无来源: source stats add nothing
* @property {number} [attackId] shared by every instance of one normal attack; 0 otherwise
* @property {Unit|null} [traitAlly]
*/
/**
* One living unit in a snapshot.
* `[id, x, y, hp, maxHp, sp, spMax, flags, anim]` (SIM §9, DESIGN §8.2).
* `flags` is the UF bitmask. `anim` is the ANIM code.
*
* @typedef {[number, number, number, number, number, number, number, number, number]} UnitSnap
*/
/**
* A knocked-out operator waiting to redeploy.
* `[id, respawnAt, respawnTime, state, row, col]`.
* `state`: 0 counting, 1 timer done and DP short, 2 timer done and the tile is taken.
*
* @typedef {[number, number, number, number, number, number]} DownSnap
*/
/**
* Element gauge or burst cooldown on one unit.
* `[id, element, fill, cooldownEnd, cooldown]`.
*
* @typedef {[number, string, number, number, number]} ElemSnap
*/
/**
* `snapshot()` (SIM §9). On the wire this object is the payload of `b.snap`,
* and `t` is sent as `gt` because the frame's `t` is the message type (DESIGN §8.2).
* `dp` is one number. `down` and `elem` are omitted when empty.
*
* @typedef {Object} FieldSnapshot
* @property {string} fieldId
* @property {number} t game seconds
* @property {UnitSnap[]} units
* @property {number} dp
* @property {number} killed
* @property {number} total
* @property {Object<string, number>} [dps]
* @property {Object} [boss]
* @property {DownSnap[]} [down]
* @property {ElemSnap[]} [elem]
*/
/**
* One WebSocket JSON frame (DESIGN §8). Client requests may carry `rid`.
* The server's `ok` or `error` echoes it. Server pushes omit it.
* Other own fields are the payload. Inbound frames are capped at 64 KB.
*
* @typedef {Object} ProtocolMessage
* @property {string} t message type
* @property {number} [rid] request id
*/
export {};