Driving a TypeScript Codebase to Zero Errors
Case study — taking a large React codebase from a long backlog of TypeScript errors to zero with months of small, non-functional merge requests, so the build could finally enforce types.
Context
A large React front end for an enterprise platform had been migrated to TypeScript in name. The files were .tsx, but the
compiler reported a long backlog of errors, so type checking was switched off in the build. New use cases were about to land on
top of it.
Problem
With type errors tolerated, types stopped meaning anything. Nobody could tell a new error from the existing noise, so regressions that the compiler would have caught reached runtime. Every developer had learnt to ignore the red squiggles. And a “fix all the types” ticket was never going to be prioritised over features.
What I did
- Made it a background habit, not a project. Over several months I shipped a steady stream of small, non-functional merge requests, each fixing one folder or one category of error. Each was easy to review, impossible to break much with, and merged within a day.
- Fixed causes, not symptoms. Where the same error appeared fifty times, the fix was usually one wrong type at a boundary, such as an API response or a shared helper. Typing the data layer properly (API Modelling) cleared whole swathes at once.
- Refused to cheat. No blanket
any, no@ts-ignorecarpet. Where a proper signature needed more context than I had, I left an explicit, searchableunknowncast and flagged it for follow-up, rather than hiding it. - Locked in progress. Lint rules stopped new errors creeping into folders already cleaned, so the count could only go down.
- Turned enforcement on at zero. Once the count reached zero, type checking became part of the build. From then on a type error failed the pipeline.
Outcome
Zero TypeScript errors, and build-time type checking switched on for good. New code got real compiler feedback, reviewers stopped arguing about types the compiler could settle, and the remaining loose casts were a short, visible list instead of an invisible swamp.
What I’d do again
- Small and steady beats heroic. It’s progressive overload for a codebase. No big-bang rewrite, just a little more load each week, and it never blocked feature work.
- Ratchet, don’t promise. A rule that prevents regressions is worth more than a plan to fix everything.
- Make the end state enforceable. The goal wasn’t “fewer errors”, it was “the build enforces types”. That’s a finish line everyone understands.
What I’d do differently
Turn on strictness per folder from the start (with a project-references or per-directory config), so cleaned areas got enforcement immediately rather than waiting for the whole codebase to hit zero.
Related notes
Shift Left Testing · Building a Low-Code BPMN Workflow Platform · SOLID React
- #typescript
- #tech-debt
- #incremental-migration
- #code-quality
- #case-study
↩ Linked from (1)
…so new use cases started from approved pieces. Hardened the codebase in parallel: TypeScript errors to zero (Driving a TypeScript Codebase to Zero Errors), a coverage drive with network-level mocks (A Testing Strategy T…
↪ Links to (5)
Related by topic
- Growingcase study
A Testing Strategy Teams Actually Adopted
Frontend craft - Growingcase study
Building a Graph Component Library
Frontend craft - Growingcase study
Chairing a Cross-Team UI Guild
Career - Growing
Removing Secrets from Git History
Frontend craft