TypeScript Refactor
A disciplined approach to TypeScript refactoring in this monorepo. Always run pre-flight-thinking before starting.
Before You Touch Anything
- Run
pnpm tsc --noEmitfrom the workspace root to establish a clean baseline. - Identify all consumers of the symbol you are changing:
grep -r "importedName" --include="*.ts" --include="*.tsx". - Check whether the file is re-exported from a package's public index (
index.ts). If yes, the change is a public API change — treat it likeapi-contract-review.
Core Patterns
Extract a Shared Utility
When the same logic appears in ≥ 2 packages:
- Create the util in
packages/shared/src/utils/<name>.ts. - Export it from
packages/shared/src/index.ts. - Replace each site with the import.
- Keep the original file with a deprecation comment until all consumers are updated.
Eliminate any and unknown Casts
- Replace
anywith the narrowest correct type or a well-named generic. - Replace
as unknown as Tcasts with a proper type guard function. - Use
satisfies(TS 4.9+) instead ofaswhen validating literal objects against an interface.
Narrow Union Types
- Prefer discriminated unions (
type Result = { ok: true; data: T } | { ok: false; error: string }) over optional fields. - Add an exhaustive
assertNever(x: never)helper in shared utils and call it inswitchdefault branches.
Improve Return Types
- Avoid implicit
voidreturns on async functions that callers might await. - Annotate exported functions with explicit return types so signature changes cause compile errors at the call site.
Monorepo-Specific Rules
- Never import from a sibling package's
src/directly — only from its published package name (e.g.,@szl/shared, not../../packages/shared/src). - After refactoring a shared package, run
pnpm --filter <changed-package> buildbefore testing dependents. - Update
CHANGELOG.mdin the package if the public API surface changes.
Verification Checklist
- [ ]
pnpm tsc --noEmitpasses with zero new errors - [ ]
pnpm lintpasses - [ ] All callers of the changed symbol have been updated
- [ ] No
@ts-ignoreor@ts-expect-errorwas added without a comment explaining why