Skip to content

Migrate to TypeScript 6 #4

@Product-nomad

Description

@Product-nomad

Context

Dependabot PR #3 proposed bumping TypeScript from 5.9.3 to 6.0.3 (alongside @types/node 22→25). The PR was closed because the jumps needed real work rather than a drive-by merge. Dependabot is now pinned to ignore major bumps for both packages (see .github/dependabot.yml) until we do this migration explicitly.

What TypeScript 6 caught

Running npm run typecheck on the v6 branch surfaced two failure classes:

1. Stricter control-flow analysis in src/cli.ts

async function main(): Promise<void> {
  let args: ParsedArgs;
  try {
    args = parseArgs(process.argv);
  } catch (err) {
    process.stderr.write(...);
    process.exit(64);
  }
  if (args.help) {        // ← TS6 now says: "Variable 'args' is used before being assigned."
    ...
  }
}

TS 6 no longer treats process.exit(64) as unconditionally terminating within a catch block for the purpose of definite-assignment analysis. This is a real latent bug: if parseArgs threw and (hypothetically) process.exit was mocked or the catch didn't exit, args would be undefined. Fix options:

  • Restructure so args is assigned in both branches (cleanest).
  • Use a non-null assertion (args!.help) — lazy, loses the check.
  • Explicit return / throw after process.exit to narrow the type.

2. Global process no longer auto-included

error TS2591: Cannot find name 'process'. Do you need to install type definitions for node?

This appears to be related to how @types/node and tsconfig.json's implicit types interact in TS 6. Likely fix: add \"types\": [\"node\"] to the compilerOptions block, or set \"moduleResolution\" appropriately. Needs investigation — may also affect other @types/* discoverability.

Acceptance criteria

  • TypeScript bumped to ^6.0.3 (or whatever is current at the time of migration)
  • @types/node bumped to match engines.node (currently >=22, so ^22.x is correct — not 25)
  • src/cli.ts main() restructured so args is definitely assigned; no non-null assertions added
  • tsconfig.json updated to declare \"types\": [\"node\"] (or equivalent) so Node globals resolve
  • npm run check passes (typecheck + lint + 102 tests)
  • CHANGELOG.md entry under Unreleased
  • DECISIONS.md entry explaining the migration trigger and any API surface changes
  • Dependabot ignore rules for these two packages removed once migration is merged

Out of scope

  • Moving engines.node above 22. Keep that separate.
  • Switching away from tsc to swc / esbuild / other compilers. Different discussion.

Notes for the next contributor

Good first issue if you're comfortable with TypeScript's strict mode. Likely 20–50 lines of code change plus config. Read `THREAT_MODEL.md` and `WAYS_OF_WORKING.md` (referenced from the README) before starting — the project has a test-first rule and the `args` restructure should include a unit test covering the "parseArgs throws" path.

Metadata

Metadata

Assignees

No one assigned

    Labels

    dependenciesPull requests that update a dependency fileenhancementNew feature or requestgood first issueGood for newcomershelp wantedExtra attention is needed

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions