Refactor Legacy TypeScript with OpenCode
Published October 20, 2026 · Editorial policy

That monolithic TypeScript service written back in 2021 is actively slowing down every feature release. You are a senior full-stack developer staring down 45,000 lines of any types, circular dependency warnings, and missing null checks, and rewriting it from scratch is not an option. You want to systematically clean up the architecture, enforce strict mode, and extract modular services without breaking production, but manual refactoring will take weeks of tedious find-and-replace work.
Here is how to use OpenCode to automate the heavy lifting of refactoring a legacy TypeScript codebase, moving from loose configurations to a strict, modern module structure in a single afternoon.
Fast-Track Implementation Plan
If you want the solution immediately without reading the theory, execute this sequence in your terminal at the root of your legacy project.
First, install OpenCode globally or run it via npx:
npx @opencode/cli@latest init
When prompted by the configuration wizard, select the following parameters:
- Target Language: TypeScript (
v4.5+orv5.0+) - Refactoring Strategy: Incremental Strictness & Module Decoupling
- AST Parser: TypeScript Compiler API (Native)
- AI Backend: Anthropic Claude 3.5 Sonnet (recommended for code generation accuracy)
Verify your installation by running a dry-run analysis on your source directory:
npx @opencode/cli analyze --src ./src --target-strictness
Expected output in your terminal:
[OpenCode] Scanning ./src...
[OpenCode] Found 142 TypeScript files.
[OpenCode] Analysis complete:
- Implicit 'any' usages: 312
- Circular dependencies detected: 8 modules
- Estimated refactoring complexity: Medium-High
[OpenCode] Ready to execute transformation pipeline. Run 'npx @opencode/cli refactor' to apply fixes.
Step 1: Enforcing Strict Nulls and Eliminating any
Legacy codebases often accumulate any types out of expediency, disabling TypeScript's primary safety net. OpenCode reads your Abstract Syntax Tree (AST), infers the correct types based on usage patterns and runtime boundaries, and replaces loose definitions without breaking downstream consumers.
Configuring the Inference Rules
Do not let OpenCode guess blindly. Create an opencode.config.json file in your root directory to set explicit boundaries for type generation.
{
"$schema": "https://opencode.ai/schemas/v1/config.json",
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true
},
"inference": {
"strategy": "conservative",
"fallbackType": "unknown",
"maxUnionTypes": 4,
"excludePatterns": ["src/legacy/third-party/*.d.ts"]
},
"output": {
"format": "ts",
"addTodoComments": true
}
}
Running the Type Hardening Engine
Execute the transformation command pointing directly to your configuration:
npx @opencode/cli refactor --config opencode.config.json --focus types
OpenCode will process files incrementally. For every ambiguous any it cannot confidently resolve, it injects an unknown type and adds an explicit // @opencode-todo: Verify type inference comment above the line so you can audit it later.
Step 2: Decoupling Circular Dependencies
Circular dependencies in legacy TypeScript lead to undefined initialization errors, memory leaks, and agonizingly slow Webpack or Vite build times. OpenCode includes a topological sorting engine that detects import cycles and refactors them using dependency injection or extraction into shared utility modules.
Run the module-decoupling command to map and break cycles:
npx @opencode/cli decouple --src ./src --max-depth 5 --strategy extract-interface
During this process, OpenCode performs these actions:
- Identifies files involved in circular import chains (e.g.,
src/models/user.tsimportingsrc/services/auth.tswhich importssrc/models/user.ts). - Extracts shared interfaces into a neutral
src/types/directory. - Rewrites relative import paths across all affected files.
Verify that your dependency graph is clean by running your bundler or test suite:
npm run build
Troubleshooting Common Blockers
Refactoring engines interact deeply with your local environment. If the process stalls or throws errors, check these common failure points:
1. Out-of-Memory Errors on Large Codebases
- The Symptom: Node.js exits abruptly with
FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memoryduring the AST analysis phase. - The Fix: Increase the Node.js memory limit for the execution command:
NODE_OPTIONS="--max-old-space-size=8192" npx @opencode/cli analyze --src ./src
2. Broken Imports After Path Aliasing
- The Symptom: OpenCode rewrites relative paths (
../../components/Button) into absolute paths, but yourtsconfig.jsonpaths mapping is misconfigured, causing compilation failures. - The Fix: Ensure your
tsconfig.jsonhas explicitbaseUrlandpathsdefined before running OpenCode:
Then run OpenCode with the{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["src/*"] } } }--respect-tsconfigflag:npx @opencode/cli refactor --respect-tsconfig
3. Over-Aggressive Type Narrowing
- The Symptom: OpenCode replaces valid union types with overly restrictive literals based on a single code path.
- The Fix: Adjust the
inference.maxUnionTypesproperty in youropencode.config.jsonup to6or8to allow broader type definitions, or lock specific files using an.opencodeignorefile matching.gitignoresyntax.
Prevention and Next Steps
Once OpenCode has modernized your types and broken your circular dependencies, you need to ensure the codebase does not regress into its previous state.
Add OpenCode's linter check directly into your continuous integration pipeline to block PRs that introduce any types or new circular imports. In your .github/workflows/ci.yml:
name: TypeScript Quality Gate
on: [pull_request]
jobs:
lint-architecture:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: '20'
- run: npm ci
- name: Run OpenCode Architecture Check
run: npx @opencode/cli lint --strict --no-cycles
By pairing OpenCode's AST transformation engine with strict CI enforcement, you turn a painful legacy codebase into a predictable, maintainable TypeScript service without halting feature development.
Related articles

Index and Query Local Docs with LangChain and ChromaDB

Benchmark Local LLMs vs Cloud APIs for VS Code Code Completion
