08.1
为什么用 TS
TypeScript 给 JS 加静态类型,在编译期发现"传错类型""访问不存在字段"等问题,重构更安心。它最终编译为纯 JS 运行。
类型即文档
函数签名里的类型本身就是最好的文档,配合 IDE 自动补全,新成员上手更快。
08.2
核心类型
基础类型、接口、联合类型、字面量类型、可选/只读修饰符,构成日常建模工具箱。
type Role = 'admin' | 'user';
interface User {
id: number;
email: string;
role: Role;
nickname?: string; // 可选
readonly createdAt: Date; // 只读
}08.3
泛型与收窄
泛型让函数/类型参数化;类型守卫(typeof/instanceof/in 或自定义 is)把宽类型收窄成具体类型。
function first(arr: T[]): T | undefined { return arr[0]; }
function isUser(u: unknown): u is User {
return typeof u === 'object' && u !== null && 'email' in u;
}
if (isUser(x)) console.log(x.email); // 此处 x 已被收窄为 User 08.4
tsconfig 配置
Node 项目的 tsconfig 应开启 strict,并正确设置模块/目标以匹配 Node 20 的 ESM。
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"strict": true,
"outDir": "dist",
"rootDir": "src"
},
"include": ["src"]
}08.5
运行 TS
开发期用 tsx(基于 esbuild,免编译即时运行)或 ts-node;生产期先用 tsc 编译到 dist 再跑 JS。
npx tsx src/index.ts # 开发热运行
npm run build # tsc 输出到 dist
node dist/index.js # 生产运行编译产物生产别跑源码
线上运行编译后的 JS,启动更快、无编译开销、依赖更干净。
08.6
装饰器
装饰器(TS 5 稳定语法)可标注类、方法、参数,NestJS 等框架大量使用它声明路由与注入。
function Log(target: any, key: string, desc: PropertyDescriptor) {
const fn = desc.value;
desc.value = function (...args: any[]) {
console.log('call', key, args);
return fn.apply(this, args);
};
}
class Svc { @Log greet(n: string) { return `hi ${n}`; } }08.7
运行时校验 zod
TS 类型只在编译期存在。外部输入(HTTP body、env)需用 zod 在运行时校验,并推导出类型。
import { z } from 'zod';
const UserSchema = z.object({ email: z.string().email(), age: z.number().int() });
type User = z.infer; // 类型由 schema 推导
const data = UserSchema.parse(req.body); // 失败抛错,成功为 User 类型 信任边界
任何来自网络/配置的数据都应经运行时校验,不要把外部输入当已类型化的值直接用。
08.8
常见陷阱与 FAQ
开启 strict 与 noImplicitAny,逐步把 any 换成具体类型或 unknown + 守卫。any 会"关掉"类型检查,是隐患。
职责不同:TS 类型管编译期;zod 管运行时边界。用 z.infer 让两者同源,避免漂移。
不建议。tsx/ts-node 适合开发;生产编译成 JS 运行更稳更快。
08.9