Agent X-Ray
RuntimeNotesAbout
Notes/代码工程/TypeScript 深度教程/第14章

第14章:JavaScript 渐进类型化 —— JSDoc、检查与声明生成

3 分钟 · 更新于 2026-09-01

第14章:JavaScript 渐进类型化 —— JSDoc、检查与声明生成

TypeScript 不要求一次把所有 .js 改成 .ts。本章建立渐进路线:先让 JavaScript 获得类型检查,再用 JSDoc 描述契约,最后按价值迁移文件或生成声明。


一、让 JavaScript 进入 TypeScript 项目

json
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "noEmit": true,
    "strict": true
  },
  "include": ["src/**/*"]
}

也可只在单个文件顶部启用:

js
// @ts-check

临时关闭单文件检查:

js
// @ts-nocheck

后者应极少使用并记录原因。

二、JavaScript 中的推断规则

TypeScript 会根据赋值、构造函数和 CommonJS 模式推断:

js
class User {
  constructor(name) {
    this.name = name;
    this.active = true;
  }
}

对象字面量在 JS 检查中可能比 TS 文件更开放;函数参数默认可选等行为也可能更宽松。迁移时不要假设 .js.ts 的检查严格度完全相同。

三、JSDoc 基础

js
/** @type {string} */
let route = "HKG-NRT";

/**
 * @param {number} left
 * @param {number} right
 * @returns {number}
 */
function add(left, right) {
  return left + right;
}

四、定义可复用类型

js
/**
 * @typedef {Object} User
 * @property {string} id
 * @property {string} name
 * @property {boolean} [active]
 */
js
/** @type {User} */
const user = { id: "1", name: "Ada" };

回调类型:

js
/** @callback Formatter
 * @param {number} value
 * @returns {string}
 */

五、泛型 JSDoc

js
/**
 * @template T
 * @param {T[]} values
 * @returns {T | undefined}
 */
function first(values) {
  return values[0];
}

约束和默认参数也可表达,但复杂度上升后 .ts 通常更清晰。

六、导入类型

js
/** @typedef {import("./types").User} User */

或者在类型位置直接使用:

js
/** @param {import("./types").User} user */
function save(user) {}

这样不会产生运行时导入。

七、JSDoc 中的 @satisfies

js
/** @satisfies {Record<string, string>} */
const routes = {
  home: "/",
  user: "/users/:id"
};

它检查约束,同时保留对象自身精确类型,作用类似 TypeScript 的 satisfies

八、类相关标签

常用标签:

  • @implements
  • @extends
  • @constructor
  • @this
  • @readonly
  • @override
js
/** @implements {Serializable} */
class UserRecord {
  serialize() {
    return "{}";
  }
}

JSDoc 只能描述,仍需真实实现满足运行时行为。

九、从宽松到严格的迁移顺序

  1. 开启 allowJs
  2. 仅对关键目录启用 checkJs
  3. 修复明显拼写和参数错误。
  4. 为公共函数、数据边界和复杂对象补 JSDoc。
  5. any 改为 unknown 并增加守卫。
  6. 迁移叶子模块为 .ts
  7. 逐步开启 noImplicitAny、严格空值等规则。
  8. 最后迁移入口、框架胶水和高耦合模块。

十、常见迁移错误

顺序添加属性

js
const options = {};
options.timeout = 3000;

改成一次性对象字面量或 JSDoc 类型,便于推断。

参数数量不一致

JavaScript 调用可多传少传,TypeScript 会按声明检查。应确认真实 API,而不是通过可选参数掩盖错误。

Object{}object

  • Object:通常过宽,不推荐。
  • {}:除 null/undefined 外几乎所有值。
  • object:非原始值。
  • Record<string, unknown>:字符串键记录,但仍有具体假设。

十一、生成 .d.ts

成熟 JS 库可在保留 JavaScript 实现的同时输出声明:

json
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": true,
    "declaration": true,
    "emitDeclarationOnly": true,
    "outDir": "dist/types"
  }
}

生成声明前应确保公共函数有充分 JSDoc,否则会产生 any 或过宽类型。

十二、抑制指令治理

js
// @ts-expect-error 第三方声明缺陷,升级 SDK 后删除
legacyApi.callUnsupportedShape();

@ts-expect-error 会在错误消失时提醒,优于 @ts-ignore。CI 可统计抑制指令并禁止无说明新增。

十三、何时不必迁移为 .ts

保留 .js + JSDoc 可能适合:

  • 向纯 JavaScript 用户发布的库。
  • 配置文件和构建脚本。
  • 团队需要渐进采用。
  • 运行环境直接消费 JS,不希望增加转译步骤。

目标是获得足够可靠的反馈,不是追求扩展名统一。

十四、练习

  1. 给一个 JS 工具文件添加 @ts-check
  2. @typedef 建模用户对象。
  3. @template 实现泛型 first
  4. 生成声明文件并检查是否出现意外 any
  5. 制定一份按目录推进的迁移清单。

十五、总结

  1. TypeScript 可直接检查 JavaScript,不必一次性重写。
  2. JSDoc 能表达参数、返回值、对象、泛型和类契约。
  3. JS 文件的推断规则比 TS 更宽松,迁移时要理解差异。
  4. 渐进迁移应从边界和叶子模块开始。
  5. .d.ts 生成结果仍需审查和消费测试。

请继续阅读:第15章:浏览器开发。


原始资料引用


  • 第13章:声明文件
  • 第15章:浏览器开发

本章目录
一、让 JavaScript 进入 TypeScript 项目二、JavaScript 中的推断规则三、JSDoc 基础四、定义可复用类型五、泛型 JSDoc六、导入类型七、JSDoc 中的 @satisfies八、类相关标签九、从宽松到严格的迁移顺序十、常见迁移错误十一、生成 .d.ts十二、抑制指令治理十三、何时不必迁移为 .ts十四、练习十五、总结原始资料引用Related Documents
苏ICP备2025204887号-2