close
Skip to main content

JavaScript → TypeScript Examples

A gallery of real before/after conversions for the patterns we see most often. Every example below is exactly what the deterministic AST engine produces — no LLM, no guessing. To convert your own code, paste it into the online converter or grab the desktop app.

require to import

Default, destructured, and rename forms all rewrite to ES module imports.

module.js
const express = require('express');
const { readFile, writeFile } = require('fs/promises');
const { x: alias } = require('./mod');
module.ts
import express from 'express';
import { readFile, writeFile } from 'fs/promises';
import { x as alias } from './mod';

module.exports to export

Object form, expression form, and named exports.x all rewrite to ES module exports.

exports.js
function add(a, b) { return a + b; }
module.exports = { add };

exports.sub = function (a, b) { return a - b; };

module.exports = function handler(req, res) { res.end('ok'); };
exports.ts
function add(a, b) { return a + b; }
export { add };

export function sub(a, b) { return a - b; }

export default function handler(req, res) { res.end('ok'); }

JSDoc to inline types

@param, @returns, optional [param], and union types lift out of comments into the signature.

math.js
/**
 * @@param {string} name
 * @@param {number} [age]
 * @@returns {string}
 */
function greet(name, age) {
  return 'Hello ' + name;
}

/**
 * @@param {string|number} id
 */
function find(id) { /* ... */ }
math.ts
function greet(
  name: string,
  age?: number
): string {
  return 'Hello ' + name;
}

function find(id: string | number) { /* ... */ }

class field detection

this.x = ... assignments in the constructor become typed class field declarations.

user.js
class User {
  constructor(name) {
    this.name = name;
    this.createdAt = new Date();
    this.loginCount = 0;
  }
}
user.ts
class User {
  name: any;
  createdAt: Date;
  loginCount: number;

  constructor(name) {
    this.name = name;
    this.createdAt = new Date();
    this.loginCount = 0;
  }
}

static property hoisting

ClassName.prop = value assignments outside the class lift into the class body as static members.

registry.js
class Registry {
  static find(id) { return Registry.entries[id]; }
}

Registry.entries = {};
Registry.DEFAULT_NAMESPACE = 'app';
registry.ts
class Registry {
  static entries: object = {};
  static DEFAULT_NAMESPACE: string = 'app';

  static find(id) { return Registry.entries[id]; }
}

private/protected inference

Identifiers prefixed with _ are inferred private; double-underscore becomes protected.

model.js
class Model {
  constructor() {
    this.id = 1;
    this._cache = new Map();
    this.__internal = true;
  }
}
model.ts
class Model {
  id: number;
  private _cache: Map<any, any>;
  protected __internal: boolean;

  constructor() {
    this.id = 1;
    this._cache = new Map();
    this.__internal = true;
  }
}

Express request handler

Inferred Request and Response types when express is imported.

server.js
const express = require('express');
const app = express();

app.get('/users/:id', (req, res) => {
  res.json({ id: req.params.id });
});
server.ts
import express, { Request, Response } from 'express';
const app = express();

app.get('/users/:id', (req: Request, res: Response) => {
  res.json({ id: req.params.id });
});

async fs/promises

Async functions keep their semantics; Promise return types are inferred from JSDoc when present.

config.js
const { readFile } = require('fs/promises');

/**
 * @@param {string} p
 * @@returns {Promise<object>}
 */
async function load(p) {
  const raw = await readFile(p, 'utf8');
  return JSON.parse(raw);
}

exports.load = load;
config.ts
import { readFile } from 'fs/promises';

async function load(p: string): Promise<object> {
  const raw = await readFile(p, 'utf8');
  return JSON.parse(raw);
}

export { load };

React functional component

JSX is detected, the file is renamed .tsx, and JSDoc prop comments lift into the parameter type.

Button.jsx
/**
 * @@param {{label: string, onClick: () => void}} props
 */
export function Button(props) {
  return <button onClick={props.onClick}>{props.label}</button>;
}
Button.tsx
export function Button(props: {
  label: string;
  onClick: () => void;
}) {
  return <button onClick={props.onClick}>{props.label}</button>;
}

React class component

this.state is detected; class field declarations are emitted.

Counter.jsx
export class Counter extends React.Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  render() {
    return <div>{this.state.count}</div>;
  }
}
Counter.tsx
export class Counter extends React.Component {
  state: { count: number };

  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }
  render() {
    return <div>{this.state.count}</div>;
  }
}

generated tsconfig.json

The engine inspects your source and emits a tsconfig tuned to the module system and runtime it detects.

tsconfig (CommonJS Node)
(input: a Node.js project using CommonJS)
tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "esModuleInterop": true,
    "strict": false,
    "skipLibCheck": true,
    "outDir": "dist"
  },
  "include": ["src/**/*"]
}

@@ts-expect-error scaffolding

Where the converted output would still fail to compile, the engine inserts an @@ts-expect-error so the project ships green.

before.ts
// converter ran require('fs') -> import fs from 'fs'
import fs from 'fs';
fs.readFileSync('nope');
after.ts
// @@ts-expect-error TS2792: Cannot find module 'fs'. Try setting moduleResolution.
import fs from 'fs';
fs.readFileSync('nope');

Try your own pattern

Pick the example above that's closest to your code and click through — or paste your own file into the converter.