TypeScript 是 JavaScript 的超集,它在 JavaScript 的基础上添加了静态类型系统和其他现代语言特性。将 TypeScript 与 Node.js 结合使用,可以显著提升代码的可维护性和开发体验,尤其是在大型项目中。本章将带你从零开始搭建 TypeScript + Node.js 环境,并探索其最佳实践。
TypeScript 为 Node.js 开发带来了许多好处:
mkdir my-ts-node-app
cd my-ts-node-app
npm init -y
npm install --save-dev typescript @types/node ts-node nodemon
typescript:TypeScript 编译器。@types/node:Node.js 核心模块的类型定义。ts-node:用于直接运行 TypeScript 文件(开发时方便)。nodemon:监听文件变化自动重启。使用以下命令生成默认配置:
npx tsc --init
然后根据需要修改,一个常见的 Node.js 配置如下:
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"lib": ["ES2022"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
"scripts": {
"start": "node dist/index.js",
"dev": "nodemon --exec ts-node src/index.ts",
"build": "tsc",
"clean": "rm -rf dist"
}
dev 脚本使用 ts-node 直接运行 TypeScript 源文件,适合开发;build 编译为 JavaScript;start 运行编译后的代码,适合生产。
创建 src/index.ts:
function greet(name: string): string {
return `Hello, ${name}!`;
}
const user: string = 'TypeScript';
console.log(greet(user));
运行 npm run dev 即可看到输出。
TypeScript 能够识别 @types/node 提供的类型,例如使用 fs 模块:
import fs from 'fs/promises';
async function readFile(path: string): Promise<string> {
try {
const data = await fs.readFile(path, 'utf8');
return data;
} catch (err) {
console.error(err);
throw err;
}
}
安装 Express 及其类型定义:
npm install express
npm install --save-dev @types/express
创建一个简单的 Express 应用 src/app.ts:
import express, { Request, Response } from 'express';
const app = express();
const port = process.env.PORT || 3000;
app.use(express.json());
interface User {
id: number;
name: string;
}
let users: User[] = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
app.get('/users', (req: Request, res: Response) => {
res.json(users);
});
app.get('/users/:id', (req: Request, res: Response) => {
const id = parseInt(req.params.id);
const user = users.find(u => u.id === id);
if (!user) {
return res.status(404).json({ error: 'User not found' });
}
res.json(user);
});
app.post('/users', (req: Request, res: Response) => {
const newUser: User = {
id: users.length + 1,
name: req.body.name
};
users.push(newUser);
res.status(201).json(newUser);
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
当使用没有类型定义的 JavaScript 库时,你可以自己编写声明文件,或者从 DefinitelyTyped 安装(@types/package-name)。如果需要自定义类型,可以创建 src/types 目录存放 .d.ts 文件。例如 src/types/custom.d.ts:
declare module 'some-library' {
export function doSomething(param: string): void;
}
在 VSCode 中调试 TypeScript 非常方便。创建 .vscode/launch.json:
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Launch Program",
"runtimeArgs": ["-r", "ts-node/register"],
"args": ["${workspaceFolder}/src/index.ts"]
}
]
}
这样可以直接调试 TypeScript 源文件。或者使用 nodemon 配合 --inspect 也可以。
为了让模块导入更清晰,可以在 tsconfig.json 中配置路径别名:
{
"compilerOptions": {
"baseUrl": ".",
"paths": {
"@/*": ["src/*"],
"@models/*": ["src/models/*"]
}
}
}
然后在代码中使用:
import User from '@/models/User';
注意:运行时需要额外工具(如 tsconfig-paths)来解析别名,安装 tsconfig-paths 并在启动时注册:
npm install tsconfig-paths
使用 node -r tsconfig-paths/register -r ts-node/register src/index.ts。
TypeScript 可以定义自定义错误类:
export class ApiError extends Error {
public statusCode: number;
constructor(message: string, statusCode: number) {
super(message);
this.statusCode = statusCode;
this.name = 'ApiError';
}
}
然后在中间件中处理:
app.use((err: Error, req: Request, res: Response, next: NextFunction) => {
if (err instanceof ApiError) {
res.status(err.statusCode).json({ error: err.message });
} else {
res.status(500).json({ error: 'Internal server error' });
}
});
运行 npm run build 生成 JavaScript 文件到 dist 目录。部署时只需上传 dist、package.json、package-lock.json 和 node_modules(或者使用 npm ci --production 安装生产依赖)。然后在生产环境运行 npm start(指向编译后的入口)。
strict 模式,获得最强的类型检查。import type 导入仅类型信息,避免运行时依赖。ESLint + @typescript-eslint 进行代码风格和质量检查。prettier 统一代码格式。