从零到一:TypeScript 遗留代码重构指南

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:为什么重构遗留代码不是奢侈,而是必要

\n

在现代 Web 开发中,TypeScript 已成为大型项目的事实标准。然而,许多公司和初创公司都面临一个问题:用纯 JavaScript 或早期非严格版本 TypeScript 编写的代码库,随着时间的推移会变成“泥潭”。缺乏类型、臃肿的函数、隐式依赖和过时的模式——所有这些都拖慢了开发速度并增加了错误数量。

\n

重构遗留代码是在不改变程序外部行为的情况下改进其内部结构的过程。在 TypeScript 的背景下,这具有特殊的价值:我们不仅清理代码,还获得了一个强大的类型系统,可以在编译阶段防止错误。在本文中,我们将分解重构的逐步计划,从初步分析到最终测试,并提供真实的 TypeScript 示例。

\n\n

1. 分析与盘点:从何开始?

\n

第一步也是最重要的一步——了解我们面对的是什么。不能只是拿起代码就全部重写。那将导致混乱和浪费时间。正确的方法是进行代码库审计。

\n
    \n
  • 确定“热点”。找到那些最常被修改或包含最多错误的模块。使用诸如 git log 或 SonarQube 之类的工具。
  • \n
  • 识别隐式类型。在遗留的 JS 代码中,经常有接受 any 或使用 @ts-ignore 的函数。列出这些地方。
  • \n
  • 检查依赖项。过时的库(例如 lodash 3.x)可能会阻碍向现代 TypeScript API 的迁移。
  • \n
  • 编写测试。在进行任何重构之前,用测试覆盖关键代码区域。这是你的“安全网”。
  • \n
\n

示例:你有一个处理用户数据的 JS 函数。与其立即重写它,不如先在 Jest 或 Vitest 上添加单元测试:

\n
// legacy.js\nfunction processUser(data) {\n    return { name: data.name, age: data.age + 1 };\n}\n\n// test.js\nimport { processUser } from './legacy';\ntest('processUser increments age', () => {\n    expect(processUser({ name: 'Alice', age: 30 })).toEqual({ name: 'Alice', age: 31 });\n});\n
\n

只有在此之后,才能开始重构。

\n\n

2. 逐步迁移到严格类型化

\n

TypeScript 提供了几个严格级别。对于遗留代码,建议从 "strict": false 开始,然后逐步提高标准。突然启用所有选项可能会破坏数百个文件。

\n

关键步骤:

\n
    \n
  • 启用 noImplicitAny。这将迫使你为所有参数和返回值显式指定类型。起初这可能会很痛苦,但正是这样你才能找到最危险的地方。
  • \n
  • 使用 strictNullChecks。错误的主要原因之一是 null 和 undefined。这个标志将迫使你显式处理它们。
  • \n
  • 添加 noUnusedLocals 和 noUnusedParameters。它们将帮助你移除死代码。
  • \n
\n

重构具有隐式类型的函数的示例:

\n
// 重构前 (遗留 JS)\nfunction calculateTotal(price, tax) {\n    return price + (price * tax);\n}\n\n// 重构后 (TypeScript)\ninterface PriceData {\n    price: number;\n    tax: number;\n}\n\nfunction calculateTotal({ price, tax }: PriceData): number {\n    if (price < 0 || tax < 0) {\n        throw new Error('Price and tax must be non-negative');\n    }\n    return price + (price * tax);\n}\n
\n

请注意:我们不仅添加了类型,还通过使用接口和验证使 API 更加安全。

\n\n

3. 摆脱反模式和陈旧

博客

书籍推荐