TypeScript 真的是 JavaScript 的"平替"吗?5 个真实项目踩坑记录
先给结论:TypeScript 不是 JavaScript 的平替,而是 JavaScript 在"生产级项目"上的上层约束层 。它编译后跑的还是 JS,运行时性能完全一致,但它把一部分本该在凌晨 3 点由 Sentry 抓的 bug,提前到了 tsc 和编辑器里。
下面用 5 个真实项目场景的踩坑记录,讲清楚它"补了什么"也"漏了什么"。
坑 1:Node 服务迁移 TS 后,类型过了,线上照样崩
场景 :老 Express 项目迁 TS,把 req.body 标成 interface UserBody { age: number },顺手删掉了原来的 typeof req.body.age === 'number' 校验。
踩坑:
客户端传 age: "twenty" 或干脆不传,TS 编译期完全不报;Express 收到的还是裸 JSON,运行时直接把字符串当数字累加,或下游 toFixed 炸掉。
教训:
TypeScript 的类型在编译后会被擦除,它不验证 HTTP、不验证 localStorage、不验证第三方接口响应。边界入口必须用 Zod / Valibot 做运行时校验,
as断言不是校验。
ini
const UserBody = z.object({ age: z.number().int() });
const body = UserBody.parse(req.body); // 绿 tsc ≠ 安全运行时
坑 2:any 和 as 泛滥,TS 变成"纸安全带"
场景 :前端接一个字段天天变的 AI 接口,为了不让编译器叫,全员 const data: any = JSON.parse(raw),或者 return resp.json() as UserProfile。
踩坑:
上游悄悄把 commonName 改成 common_name,TS 一声不吭,组件里 user.commonName 编译通过,运行时拿到 undefined,列表页白屏。
教训:
any一出现,类型病毒式传播,比纯 JS 还危险------因为给人"我已经类型安全了"的错觉。- 原型期用
unknown+ 收窄,或先用 Zod schema 兜住,等接口稳定再反推类型。 - ESLint 里
@typescript-eslint/no-explicit-any和禁掉滥用as应尽早开。
坑 3:老 JS 项目"渐进迁移",allowJs 开了一年等于没迁
场景 :10 万行 JS 老仓库,按教程开 allowJs: true, strict: false,文件一个个改 .ts。
踩坑:
- 前 3 个月只改了工具函数,业务文件全是
any隐式推导; - 新人以为"项目是 TS 了"就放心改,结果
strictNullChecks没开,user.name.toUpperCase()在user为 null 时依旧线上崩; - 重构时编译器没拦住,因为一半代码根本没进类型检查。
教训:
渐进迁移的终点必须是 strict: true。建议路线:
allowJs共存 → 2. 改.ts时顺手开noImplicitAny→ 3. 按目录开strictNullChecks→ 4. 全量strict。
不走到strict,迁移就是 theater(表演)。
坑 4:ERP 前端生产构建 66 个 TS 错误,全是"类型漂移"
场景 :React + Redux 前端迭代两年,中间改过领域模型:status → employmentStatus、phone → phoneNumber。
踩坑:
生产 tsc 一次性爆出 66 个错:
- 旧组件还在读
user.status - 测试里 mock 的 Redux state 缺了后来加的 3 个 slice
- React Router v6 的
Outlet改造后旧测试用例类型对不上
正面价值:
这 66 个错误恰恰是 TS 的功劳------换 JS 版本项目,这些全是"点进去某页面控制台红一片"才发现。踩坑点在于类型没当成活文档维护 :接口改名要用 IDE 的 F2 rename 全量改,不要手动 find-replace;测试 mock 要用 DeepPartial<RootState> 工厂函数,别手写裸对象。
坑 5:小脚本和落地页强行上 TS,tsconfig 比业务代码长
场景:运营要一个 300 行的 Cloudflare Worker 转发脚本,leader 说"统一都用 TS"。
踩坑:
- 配
tsconfig、装@types/*、接 CI 的tsc --noEmit,花半天; - 需求两周后废弃,类型投资全沉没;
- 对比同规模纯 JS 文件,
node script.js直接跑,省掉整个构建心智。
教训:
TS 的盈亏平衡点大概在 单人维护超过 3 周 / 代码超 500--1000 行 / 贡献者 ≥ 2。低于这条线,JS + JSDoc 足够;高于这条线,TS 的重构安全感和入员 onboarding 收益才覆盖得了配置成本。
回到开头:是不是平替?
| 维度 | JavaScript | TypeScript |
|---|---|---|
| 运行时表现 | 原生 | 编译成 JS,零差异 |
| 抓 bug 时机 | 运行时(常是生产) | 编辑/编译期 |
| 重构 1 个字段改 47 处 | grep+祈祷 | 编译器给清单 |
| 外部输入校验 | 自己写 | 仍要自己写(Zod 等) |
| 小脚本/落地页 | 合适 | 过度配置 |
| 3 人+维护半年以上 | 维护税滚雪球 | 回本 |
所以更准确的说法是:
- JS 是执行层事实标准,TS 是开发期契约层;
- TS 替代不了 JS,因为它"运行不了";JS 也替代不了 TS 在中型以上团队里的可维护性收益。
- 叫它"平替"会把人带偏------TS 是给 JS 项目买的编译期保险,不是换一辆车,是给同一辆车加装仪表盘和车道偏离预警,但油门刹车还是原来那套。