Write a JavaScript to TypeScript Migration Prompt That Infers Real Types
Build a JavaScript to TypeScript migration prompt that infers types from call sites, ranks files by any-risk, and locks a per-file output contract.
The fastest way to ruin a JavaScript to TypeScript migration is to let the model type everything as any. It compiles, the PR is green, and you've gained nothing but a false sense of safety. A real JavaScript to TypeScript migration prompt does the opposite: it infers types from how each function is actually called, refuses any unless it's justified, and converts file by file in risk order.
Most guides online lean on a tool. Grit, ts-migrate, Cline with one model. They work, but they're tied to one stack and one workflow, and none of them tells you which files to convert first or where the model quietly gave up.
A prompt fixes the part the tools skip: the judgment.
Why a blind .js-to-.ts rename buys you nothing
Rename a file to .ts, add any wherever the compiler complains, and you've technically migrated. You've also thrown away the entire point of TypeScript. The types that matter, the ones that catch the bug before runtime, come from inference, and inference needs context the model has to be forced to gather.
The stance here is simple. A migration that adds any to silence errors is worse than no migration, because now the code looks typed and isn't. Every any is a hole the type checker can't see through. A good prompt treats any as a failure to infer, not a valid output, and makes the model show its work.
What a JavaScript to TypeScript migration prompt does
A JavaScript to TypeScript migration prompt is a per-file type inferrer that reads a module plus its call sites, infers concrete types, ranks files by conversion risk, and emits typed code against a contract that forbids unjustified any. It works in slices so types propagate outward cleanly.
Run it to:
- Rank files by
any-risk and dependency order so leaves convert first - Infer parameter and return types from real call sites, not guesses
- Forbid
anyin the contract unless the model justifies each one - Add shared interfaces once and reuse them across converted files
- Flag dynamic, runtime-shaped objects that need a manual type
- Keep
allowJson so JS and TS coexist mid-migration
The risk ranking is what keeps a thousand-file migration from collapsing into a wall of cascading errors.
Anatomy of the prompt
Variables
{{js_source}} → the JavaScript file(s) to convert
{{call_sites}} → how the module is used elsewhere (for inference)
{{ts_config}} → strictness flags in play (strict, noImplicitAny)
{{shared_types}} → optional: existing interfaces to reuse
Prompt
Role: you are a JavaScript-to-TypeScript migration engineer.
Task: convert {{js_source}} under {{ts_config}}.
Rules:
- Infer types from {{call_sites}}; never default to any.
- Each any must carry a one-line justification or it's rejected.
- Flag runtime-shaped objects for a manual type decision.
Output contract
Return: typed file + the any-justification list + risk notes.
1. Set the strictness up front
Put strict and noImplicitAny in {{ts_config}}. The model types more carefully when it knows the compiler will reject loose types, the same way you'd write differently knowing the linter's on.
2. Feed the call sites
Inference is only as good as the context. Paste how the module gets used into {{call_sites}} so the model types a parameter from its real arguments, not from a hopeful guess.
3. Read the any-justification list
Every any the model emits comes with a reason. That list is your review queue, the spots where the code is too dynamic to type cleanly and you need to decide.
The difference between a real migration and a cosmetic one is how you handle any. Let it slip in silently and you've shipped untyped code wearing a TypeScript badge. Force the model to justify each any in the contract, and the migration surfaces exactly where the types are genuinely hard. Those spots are where a human should look, not where the model should bluff.
Prompt-craft patterns for type migration
Infer from usage, not from the definition alone. A function's parameter types live in how it's called. A prompt that only reads the function body guesses; one that reads {{call_sites}} infers. Force the call-site pass.
Ban any by contract. Don't ask politely. Make the output contract reject any unless it carries a justification. Otherwise the model takes the easy path on every ambiguous parameter.
Convert leaves before roots. A shared module typed last means every file that imports it gets re-checked. Rank by dependency order so types flow outward and each conversion stays local.
Where Claude and GPT-4o diverge on strictness
The two models treat strict differently. Claude tends to honor a strict: true instruction and infer narrower types, often introducing a union or a generic where GPT-4o would reach for any. GPT-4o produces clean code fast but defaults to loose types unless the no-any rule sits on the final line of the prompt, where recent-token weighting keeps it in attention.
Both models struggle with the same thing: objects whose shape is decided at runtime. A config parsed from JSON, a payload that varies by branch. Neither should invent a type there, so the contract routes those to the manual-review list. OpenAI's own guidance notes that loosely specified outputs drift; restate the strictness contract for GPT-4o, and pin the model version, because a converter that started accepting any more freely after an update is a regression hiding behind a green build.
How this fits a larger migration
Typing a codebase is often one phase of a bigger upgrade. The staging logic, convert in reversible slices, mirrors any framework move. For the broader pattern, the framework migration prompt covers staged rollout, and if you're also changing the language's idioms rather than just adding types, the cross-language refactor prompt handles that case.
Variables you'll set
| Variable | Required | What it is |
|---|---|---|
{{js_source}} | Yes | The JavaScript file or files to convert |
{{call_sites}} | Recommended | How the module is used elsewhere |
{{ts_config}} | Yes | Strictness flags in play |
{{shared_types}} | Optional | Existing interfaces to reuse |
Getting started
- Copy the structure into ChatGPT, Claude, or Gemini.
- Set
{{ts_config}}with your real strictness flags. - Paste the module and its
{{call_sites}}so inference has context. - Run it and read the any-justification list before the code.
- Resolve each justified
anyby hand or accept it deliberately. - Convert leaf files first, then work inward as shared types settle.
For a migration spanning hundreds of files with risk ranking and staging already wired in, the Framework Migration Harness Playbook runs the full sequence.
Browse the migration prompt packs →The Framework Migration Harness Playbook does this end-to-end: a {{source_framework}} and {{target_framework}} pair feeds a staged plan where each file ships and verifies independently, so a JS-to-TypeScript pass converts leaves first and propagates shared types outward instead of landing a thousand anys in one PR. It's part of The Complete AI Prompts Bundle, a one-time lifetime license to the whole catalog plus future packs, worth it if you run more than one migration a year.
Once files are typed, the next job is confirming the typed code behaves like the JavaScript it replaced. The Cross-Language Refactor Harness locks a per-file output contract for that behavior check. New to buying packs versus assembling your own? Start with how to choose a reusable AI prompt pack.
See the Cross-Language Refactor Harness →Common questions
Won't the model just type everything as any?
How do you migrate a large JS codebase without breaking everything?
Does Claude or GPT-4o follow strict mode better?
Get the prompt packs this guide is built on
Ready-to-paste prompts with documented variables and usage guides for ChatGPT, Claude, and Gemini. One-time payment, own it forever.
More prompt guides

Gemini vs Claude for Long-Context Code: Window or Accuracy
The honest framing of Gemini vs Claude for long-context code isn't which model is smarter. It's a tradeoff between two different things: how much code you can fit in one prompt, and how often the mode…

An OKR Drafting Prompt That Catches Vanity Key Results
An OKR drafting prompt has to fight the model's strongest instinct: handing back something that sounds like a goal but can't be measured. Ask any model for key results and you'll get "increase user en…

A Technical Design Doc Prompt That Holds the RFC Structure
A technical design doc prompt earns its keep when it stops every author from inventing a new doc structure. Context, the options you considered, why you picked one, what breaks, how you roll it out. S…