Skip to main content
Ai promptsMigration promptsTypescriptJavascript

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.

PPromptsCart Team·August 10, 2026·Updated August 10, 2026·7 min read

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 any in 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 allowJs on 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.

Treat every `any` as a flagged decision

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

VariableRequiredWhat it is
{{js_source}}YesThe JavaScript file or files to convert
{{call_sites}}RecommendedHow the module is used elsewhere
{{ts_config}}YesStrictness flags in play
{{shared_types}}OptionalExisting interfaces to reuse

Getting started

  1. Copy the structure into ChatGPT, Claude, or Gemini.
  2. Set {{ts_config}} with your real strictness flags.
  3. Paste the module and its {{call_sites}} so inference has context.
  4. Run it and read the any-justification list before the code.
  5. Resolve each justified any by hand or accept it deliberately.
  6. 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
Skip the setup

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.

Get the Framework Migration Harness Playbook

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
FAQ

Common questions

Won't the model just type everything as any?
It will if you let it, and that's the trap. A JavaScript to TypeScript migration prompt forbids `any` in the output contract except where it's explicitly justified, and forces the model to infer types from call sites and return values instead. The contract makes every `any` a flagged decision, so you can see where the model gave up rather than discovering it later when `strict` mode lights up.
How do you migrate a large JS codebase without breaking everything?
File by file, ranked by risk, with `allowJs` enabled so JavaScript and TypeScript coexist during the migration. The prompt ranks files so leaf modules with no dependents convert first and shared types propagate outward. Converting an entry point before its dependencies is how you get a hundred cascading type errors at once.
Does Claude or GPT-4o follow strict mode better?
Claude tends to honor a `strict: true` instruction and infer narrower types when the contract names it; GPT-4o reaches for `any` or loose unions unless the no-`any` rule is restated on the final line of the prompt. Both degrade on deeply dynamic code (runtime-shaped objects), so the prompt should flag those for a manual type rather than guess. Pin the model version either way.
Stop reading. Start shipping.

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.