Skip to content

PRODUCTION SKILLS · LESSON 21 OF 24

Migrate JavaScript and consume declarations

Introduce checking incrementally while understanding declaration files.

  • allowJs
  • checkJs
  • JSDoc
  • .d.ts

Your runnable checkpoint

Extract into a new practice folder. Each download includes complete source, package.json, tsconfig.json and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.

Install with npm install. Run npm run check, then npm run build. Terminal lessons use npm start; browser lessons use npm run serve and http://127.0.0.1:4173.

1. Understand the concepts

You can adopt TypeScript incrementally. allowJs includes JavaScript source in the project; checkJs reports type errors in that source. JSDoc annotations provide contracts before you rename a file to .ts.

Begin at useful boundaries such as utility functions and data adapters. Add types, fix uncertain values, run tests and migrate one module at a time. A large collection of any annotations hides risk instead of documenting it.

A .d.ts file describes an existing runtime module. It does not implement that module. Packages may ship declarations themselves; otherwise maintained @types packages can supply them. Verify that declarations match the runtime package version.

This example checks the actual JavaScript implementation using JSDoc and imports it from TypeScript. The build emits both files into dist. Do not invent a declaration to claim that a missing or incompatible library works.

2. Build and run

These are complete files. Preserve the paths shown below, including the src directory. The package and compiler configuration are included at the end.

src/main.ts

src/main.ts
import { subtotal } from "./legacy.js";
console.log(subtotal(50, 3));

src/legacy.js

src/legacy.js
/**
 * @param {number} price
 * @param {number} quantity
 * @returns {number}
 */
export function subtotal(price, quantity) { return price * quantity; }

package.json

package.json
{
  "name": "edupoly-typescript-migrate-javascript",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "check": "tsc --noEmit",
    "build": "tsc",
    "start": "node dist/main.js"
  },
  "devDependencies": {
    "typescript": "5.9.3"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": [
      "ES2022",
      "DOM"
    ],
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noEmitOnError": true,
    "verbatimModuleSyntax": true,
    "rootDir": "src",
    "outDir": "dist",
    "sourceMap": true,
    "types": [],
    "allowJs": true,
    "checkJs": true,
    "moduleDetection": "force"
  },
  "include": [
    "src/**/*"
  ]
}

3. Trace the code and check the result

  1. allowJs includes legacy.js in the project.
  2. checkJs checks its JSDoc parameter and return contracts.
  3. The TypeScript caller receives the function’s numeric signature.

Expected result

150

4. Practice independently

Rename legacy.js to legacy.ts and replace JSDoc types with annotations.

Show the explanation

Use export function subtotal(price: number, quantity: number): number { return price * quantity; }. Keep the caller’s .js import because it names the emitted file.

When something goes wrong

If tsc is missing, run npm install in the folder containing package.json. If the output looks old, rebuild before running. Read the first compiler diagnostic and check the indicated field or argument. For module errors, preserve the .js extensions in imports. Browser examples must run through the included local server, not directly as TypeScript files.

Read the related official TypeScript documentation ↗