Course outline · 0% complete

0/26 lessons0%

Course overview →

Migrating a JavaScript File

lesson 8-2 · ~11 min · 25/26

JS to TS in four moves

Because every JavaScript file is nearly valid TypeScript, migration is incremental:

  1. Rename file.js to file.ts. It mostly compiles as is
  2. Annotate parameters, the compiler flags each implicit any for you under strict mode
  3. Extract interfaces for the object shapes flowing through the code, like Unit 3
  4. Chase the remaining errors, most are real latent bugs being surfaced

Here is step 3 in action. The JavaScript version passed bare objects around, the TypeScript version names the shape:

interface Item {
  name: string;
  qty: number;
}

function receipt(items: Item[]): string

Teams migrate large codebases file by file this way, TypeScript and JavaScript coexist in one project while it happens.

1. rename .js → .ts2. annotate parameters4. fix surfaced bugs3. extract interfaces
The migration loop: rename, annotate, extract shapes, then fix what the compiler surfaces.

A migrated function, fully typed

This is step 3 of the migration finished: the shape has a name and both the parameter and return type are annotated.

interface Item {
  name: string;
  qty: number;
}

function receipt(items: Item[]): string {
  const lines: string[] = [];
  for (const item of items) {
    lines.push(item.name + " x" + item.qty);
  }
  return lines.join(", ");
}

console.log(receipt([{ name: "pen", qty: 2 }, { name: "pad", qty: 1 }]));

Output

pen x2, pad x1

Why migration can be gradual

  • Delete the Item interface and both annotations and the program still runs identically. Types are erased at compile time (lesson 1-1), so removing them changes checking, never behavior.
  • That property is what lets a team migrate one file at a time. A half-typed codebase is a working codebase.
  • The annotations that remain are doing real work. Passing { name: "pen" } with no qty is now an error, and so is reading item.quantity.

Annotating an untyped function

totalWithShipping already worked as JavaScript. Migration means annotating both parameters and the return type, leaving the body and the output untouched.

function totalWithShipping(prices: number[], shipping: number): number {
  let sum = 0;
  for (const p of prices) {
    sum += p;
  }
  return sum + shipping;
}

console.log(totalWithShipping([10, 20], 5));
console.log(totalWithShipping([3], 2));

Output

35
5

What changed and what did not

  • Only the first line changes. Both parameters and the return type gain annotations, and every other line is the original JavaScript.
  • prices holds numbers, so its type is number[] as in lesson 2-1.
  • The annotations rule out the accident this function was open to before, where a caller passing ["10", "20"] would have concatenated strings and returned "1020" + 5.

What 30 errors after a rename actually are

They are mostly implicit-any parameters and latent shape bugs the compiler is surfacing, and the right response is to fix them incrementally.

TypeScript parses JavaScript syntax without complaint, so the errors are not syntax problems. They come from the checker demanding parameter types and flagging inconsistent object shapes, and each fix either documents or repairs something that was silently risky before.

Two reassurances are worth holding onto. These are compile-time messages, not crashes, so the program you had yesterday still runs. And the count going down is real progress rather than busywork, because every annotation you add makes the next file easier to migrate.