How to Seamlessly Migrate a Node.js Project to TypeScript: Step‑by‑Step Guide
This article walks you through converting an existing Node.js codebase to TypeScript, covering directory restructuring, TypeScript and ESLint setup, handling common import and type errors, configuring debugging in VSCode, and gradually strengthening type safety while preserving project functionality.
Introduction
If you have a Node.js project and want to refactor it with TypeScript, this guide will help you. It skips the basics of why TypeScript is popular and focuses on practical steps: directory layout, TypeScript‑ESLint configuration, tsconfig settings, debugging, and common error handling.
Step 1 – Adjust Directory Structure
Because TypeScript needs compilation, separate a
srcfolder for source files and a
distfolder for compiled output. A typical layout is:
|-- assets # images, videos, etc.
|-- bin # CLI entry, e.g., #!/usr/bin/env node
|-- dist # compiled files (main points to package.json)
|-- docs # documentation
|-- scripts # script files referenced in package.json
|-- src # only .ts files; other assets go to templates
|-- sub # sub‑directories
| └── ...
|-- cli.ts # CLI entry file
|-- index.ts # API entry file
|-- templates # json, html, etc.
|-- tests # test files
|-- typings # custom .d.ts files for missing declarations
|-- .eslintignore
|-- .eslintrc.js
|-- .gitignore
|-- package.json
|-- README.md
|-- tsconfig.jsonStep 2 – Install and Initialise TypeScript
Run the following commands in the project root:
npm i typescript -D # install as a dev dependency
node ./node_modules/.bin/tsc --init # generate tsconfig.json
# or, if TypeScript is installed globally:
# tsc --initThe generated
tsconfig.jsoncan be customised; a recommended baseline is provided later.
Step 3 – Adjust Source Files
Rename .js to .ts
Rename all JavaScript files to
.tsand move them into
src. Tools like
gulpcan automate this.
Extract Template Files
Files that are not compiled by TypeScript (e.g., .json, .html) should be placed in the
templatesdirectory.
Update package.json scripts
{
"scripts": {
"build": "tsc",
"watch": "tsc --watch"
}
}Now
npm run buildor
npm run watchcompiles the project.
Step 4 – TypeScript Code Standards
Install ESLint for TypeScript:
npm i eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin -DCreate
.eslintrc.jswith a minimal configuration:
module.exports = {
parser: '@typescript-eslint/parser',
extends: ['plugin:@typescript-eslint/recommended'],
env: { node: true }
};Optionally extend the
standardrule set:
npm i eslint-config-standard eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-standard -D module.exports = {
parser: '@typescript-eslint/parser',
extends: ['standard', 'plugin:@typescript-eslint/recommended'],
env: { node: true }
};Integrate ESLint into VSCode for on‑save fixing and real‑time hints.
Step 5 – Resolve Common Errors
Missing Node.js declarations
Install
@types/node:
npm i @types/node -DImport syntax issues
Depending on the module system, use one of the following import styles (with
esModuleInteropenabled):
import * as mod from 'mod' import { foo } from 'mod' import mod from 'mod'(default export)
import mod = require('mod')(for
export =modules)
When
esModuleInteropis false, prefer
import * as path from 'path'instead of default imports.
No declaration files for third‑party packages
Run
npm i @types/packagename -D. If none exist, create a simple declaration in
typings/packagename.d.ts:
declare module 'packagename';Class property initialization
Declare class fields before using them, e.g.:
class Person {
name: string;
constructor(name: string) {
this.name = name;
}
}For quick migration, you can temporarily use
anybut should replace it with proper interfaces later.
Implicit any and unused parameters
Explicitly type function parameters or rename unused ones to
_or prefix with an underscore.
‘this’ typing problems
Declare
thisas the first parameter in functions that use it:
export default function(this: any, one: string) {
this.name = 'haha';
}Step 6 – Debugging Configuration
Debug compiled
distfiles with a VSCode
launch.jsonlike:
{
"configurations": [{
"type": "node",
"request": "launch",
"name": "debug",
"program": "/path/to/project/dist/cli.js",
"args": ["xx"]
}]
}Or debug TypeScript directly using
ts-node:
{
"configurations": [{
"type": "node",
"request": "launch",
"name": "debug",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/src/cli.ts", "xx"]
}]
}Step 7 – Strengthen Types and Eliminate any
Gradually replace
anywith proper interfaces or type aliases. While it’s unrealistic to remove every
anyin a JavaScript‑origin project, reducing them improves type safety and IDE assistance.
Conclusion
The guide demonstrates a practical workflow for converting a Node.js project to TypeScript, covering directory layout, tooling, common pitfalls, debugging, and incremental type improvement. Although the scope is limited to typical Node.js scenarios, the steps should be adaptable to many projects.
References
[1]standard: https://standardjs.com/readme-zhcn.html
[2]ts-node: https://github.com/TypeStrong/ts-node#visual-studio-code
WecTeam
WecTeam (维C团) is the front‑end technology team of JD.com’s Jingxi business unit, focusing on front‑end engineering, web performance optimization, mini‑program and app development, serverless, multi‑platform reuse, and visual building.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.