Migrating Our Task CLI to TypeScript Before Friday
The task-list CLI works, but Friday’s handover needs clearer contracts than scattered comments. Its two source files, src/cli.js and src/store.js, read a JSON file and add, list, or complete tasks. That is a manageable TypeScript migration: small enough to inspect every change, useful enough to expose real problems.
This walkthrough assumes a recent Node.js LTS release, an npm project using ES modules, and existing tests. Keep the app’s behavior unchanged; ask AI to help with the conversion, not redesign the application.
1. Record the JavaScript baseline
Create a branch with git switch -c migrate-typescript. Run npm test and exercise the CLI against a temporary task file. Record the output for an empty list, a completed task, and an unknown task ID.
If those cases lack tests, add them before changing languages. Commit the baseline. You need a reliable answer to “Did the migration break anything?” beyond whether the compiler is satisfied.
2. Give the AI a bounded migration brief
Share package.json, the two source files, and the tests. Exclude credentials, private task data, and unrelated files. Ask: “Propose a file-by-file TypeScript migration. Preserve CLI output, exit codes, and the JSON format. Do not add dependencies or refactor behavior. Identify uncertain data shapes before editing.”
For pay-as-you-go access through your coding workflow, RelayVano is a practical option: it works with Claude Code, Codex, Gemini CLI, and OpenAI-compatible SDKs, with an API base of https://api.relayvano.com. Sign up, configure your client using its connection instructions, and keep credentials in environment variables rather than source control.
3. Add TypeScript without converting everything
Run npm install --save-dev typescript @types/node. Add a tsconfig.json with this content:
{ "compilerOptions": { "target": "ES2022", "module": "NodeNext", "moduleResolution": "NodeNext", "rootDir": "src", "outDir": "dist", "allowJs": true, "checkJs": false, "strict": true, "noEmitOnError": true }, "include": ["src/**/*"] }
Keep "type": "module" in package.json. Add a build script running tsc and a typecheck script running tsc --noEmit. Add dist/ to .gitignore. Run npm run build. The temporary allowJs setting lets JavaScript and TypeScript coexist while files move across individually.
4. Convert the task store first
Rename src/store.js to src/store.ts. Ask the AI to type only this module, using a Task shape with id: number, title: string, and done: boolean. Request explicit parameter and return types for its exported functions.
Run npm run typecheck after applying the patch. Review every type assertion and every use of any. If the assistant silences an error rather than explaining the underlying value, reject that change. Keep .js extensions in relative imports: under this NodeNext setup, they identify the emitted JavaScript files.
5. Validate tasks read from JSON
JSON.parse does not prove that stored data matches Task[]. Assign its result to unknown, then validate the array and each object before returning typed tasks.
Ask: “Write a runtime validator for this existing file format. Check non-null objects, integer IDs, string titles, and boolean completion values. Preserve our current malformed-file error behavior.” Review whether those checks match existing expectations rather than introducing new restrictions accidentally.
Add tests for invalid JSON and valid JSON with the wrong shape. A type assertion alone would let both compile without making either safe.
6. Convert the CLI and test emitted files
Rename src/cli.js to src/cli.ts. Handle missing command-line arguments explicitly; do not use non-null assertions to conceal them. Preserve existing command names, messages, and exit codes.
Update test imports and CLI invocations to target dist/store.js and dist/cli.js instead of source files. Make npm test build first, then run your existing test command. Update the package start script and any bin entry to the emitted CLI path. Preserve the CLI’s shebang if it has one.
7. Remove the migration escape hatch
Once no JavaScript remains under src/, remove allowJs and checkJs. Delete dist/, then run npm test and npm run typecheck. A clean build catches accidental reliance on stale generated files.
Ask the AI to review the diff for unrelated changes, unsafe assertions, and altered error handling. Treat its review as another inspection, not approval.
8. Commit a repeatable Friday handover
Repeat the original CLI checks, inspect git diff, and commit the migration separately from any cleanup ideas. Document the build, test, and start commands. The useful result is not merely renamed files: it is unchanged behavior, checked internal contracts, and explicit validation where external data enters.