Guided Tutorials
Migrate JavaScript to TypeScript
Add type safety to an existing JavaScript project file by file with proper type definitions. This tutorial migrates a src/utils/ directory, but the same approach scales to an entire codebase.
What you'll learn
- How to plan a bottom-up migration order based on file dependencies
- How to configure TypeScript for incremental migration with mixed JS/TS files
- How to prompt Autohand to generate proper type annotations and interfaces
- How to review generated types and fix compiler errors iteratively
Before you start
- Autohand Code installed
- Node.js 18 or newer
- A JavaScript project with a
package.json - Git with a clean working tree for clean diffs
Plan the migration order
The safest migration order is bottom-up: leaf modules with no internal dependencies first, then the modules that depend on them, then the entry points last.
Ask Autohand to map the dependency graph for your target directory:
You will get output like:
Work through this list in order. Each file you migrate reduces the unknown surface area for the next one.
Configure TypeScript
Before migrating any files, set up a TypeScript configuration that allows mixed JS/TS files. This means the project keeps building throughout the migration.
The resulting tsconfig.json will look something like this:
With allowJs: true, TypeScript is happy to import from both .js and .ts files. You can migrate one file at a time without breaking anything.
Run the migration prompt
Start with the first leaf module from your dependency order. The prompt instructs Autohand to rename the file, add annotations, and extract interfaces.
For the second layer of files, reference the already-migrated types:
Tip: If Autohand generates a type you are not sure about, ask it to explain: autohand "Explain the ValidationResult interface you just created and why each field is typed the way it is."
Review generated types
Here is what a typical migration output looks like. The original JavaScript:
The migrated TypeScript:
The key things to check in each migrated file are:
- No
anytypes unless absolutely unavoidable (and if so, there should be a comment explaining why) - Interfaces are placed in the file where the shape is first defined, or in a shared
types.tsif used across multiple files - Optional fields are marked with
?, not typed asT | undefined - Function return types are explicit on public exports
Fix type errors
After migrating each file, run the TypeScript compiler to catch errors before moving forward.
When type errors appear, paste them back to Autohand:
Autohand will read the file, understand the context around each error, and propose specific fixes. For the errors above, it might tighten the input validation upstream or add a type guard to narrow the union type.
Tip: If you see many TS7006 implicitly has any type errors, it means the function parameters were not typed. Rather than fixing them one by one, run the original migration prompt again with stricter instructions: autohand "Re-migrate this file and ensure every parameter has an explicit type annotation."
Migrate the next batch
Once the first directory is done and the compiler reports zero errors, move to the next area of the codebase. Update the migration prompt to include context from what you have already done:
When the entire codebase is migrated, tighten the TypeScript config:
Setting allowJs: false at the end ensures no new JavaScript files can slip into the codebase going forward. Run npx tsc --noEmit one final time and fix any remaining errors before merging.
What you learned
- You mapped file dependencies and migrated leaf modules first to avoid breaking imports
- You configured TypeScript for incremental migration with
allowJs: true - You prompted Autohand to add type annotations, extract interfaces, and avoid
any - You tightened the TypeScript config after completing the migration to prevent new JS files