TypeScript 真的是 JavaScript 的"平替"吗?5 个真实项目踩坑记录

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:anyas 泛滥,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。建议路线:

  1. allowJs 共存 → 2. 改 .ts 时顺手开 noImplicitAny → 3. 按目录开 strictNullChecks → 4. 全量 strict
    不走到 strict,迁移就是 theater(表演)。

坑 4:ERP 前端生产构建 66 个 TS 错误,全是"类型漂移"

场景 :React + Redux 前端迭代两年,中间改过领域模型:statusemploymentStatusphonephoneNumber

踩坑

生产 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 项目买的编译期保险,不是换一辆车,是给同一辆车加装仪表盘和车道偏离预警,但油门刹车还是原来那套
相关推荐
泡海椒16 分钟前
响应自动序列化:JSON 响应一键转 Java 实体对象,JQuick-Curl 第三方接口调用不再手动解析
后端·github
大白8016 分钟前
"速度"与"质量"不是选择题——我们如何用 CI/CD 同时保住两者
后端
八苦21 分钟前
用 runtime-async 写一个支持 async/await 的轻量脚本引擎
后端
ttwuai2 小时前
Go 后台附件迁到对象存储后,path、cdnUrl 和 tenant_id 怎么一起验?
开发语言·后端·golang
dear_bi_MyOnly2 小时前
函数模块化:企业级项目高效之道
c++·后端·学习
2601_962073972 小时前
苍穹外卖-day07(Spring Cache & 购物车业务逻辑)
java·后端·spring
Sincerelyplz3 小时前
【Pipecat】基于Pipecat的voice agent实践
前端·后端·agent
摇滚侠3 小时前
《SpringBoot 3:入门与应用实战》第 10 章 REST 服务请求与调用 Reactor 与 WebFlux 笔记 28
spring boot·笔记·后端
唐青枫3 小时前
别只会用 put:Zig HashMap 从键值查找到高频统计实战
后端