找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖

1871

积分

0

好友

259

主题
发表于 4 天前 | 查看: 16| 回复: 0

作为一个在 TypeScript 和 JavaScript 项目上都有丰富经验的开发者,我个人的偏好更倾向于后者。

这并非是因为我不喜欢为变量或函数定义类型。恰恰相反,我对此非常推崇,甚至在编写 Ruby 代码时也会保持这个习惯。

但问题的核心在于,我不认为“类型”应该成为代码本身的一部分。在我看来,代码应当只关注其行为逻辑——它是什么,以及它做什么。而那些描述代码的“元信息”,例如“这是一个字符串”或“那是一个整数”,其实更适合作为文档的一部分,以注释的形式存在。毕竟,为代码编写文档本就是应有之义。那种“不要在代码中写太多注释”的说法,实在是一种误导。

我坚信,你应该尽可能地为函数、值对象、类和各种结构添加注释(当然,要简洁明了,通常一两句话就足够了)。这就自然而然地引出了我们今天要讨论的工具——JSDoc。

在为 JavaScript 编写文档时,JSDoc 是首选标准。即便你从未用它来生成 API 文档网站,JSDoc 注释也能被众多现代开发工具和编辑器识别并解析。说到这里,就不得不提到 TypeScript。

等等,你不是更喜欢 JavaScript 吗?怎么又聊起 TypeScript了?

原因在于,即使你编写的是纯 JavaScript 文件,只要配合使用 JSDoc,TypeScript 引擎同样能为你提供强大的类型检查功能。这听起来可能有点绕,但效果却非常实在。

来看一个例子。在标准的 TypeScript 中,声明一个字符串变量通常这样写:

let str: string

str = “Hello world”
str = 123 // 这会引发类型错误!

然而,在纯 JavaScript (.js) 文件中,你只需在文件开头添加 // @ts-check,并用 JSDoc 标注类型,就能获得完全相同的类型检查效果:

// @ts-check

/** @type {string} */
let str

str = “Hello world”
str = 123 // 这同样会报类型错误!

如果你使用的是 VSCode 或 Zed 这类编辑器,类型提示和错误通常会自动显示。不过,我建议你通过 npm install typescript -D 在项目中安装 TypeScript。这样,你就能在 CI/CD 流程中独立运行 TypeScript 编译器 (tsc) 来执行类型检查或生成类型声明文件。

在我的 package.json 中,通常会配置这样一个脚本:

“build:types”: “npx tsc”

你可以通过在项目根目录创建 jsconfig.json 文件来配置类型检查的行为,例如:

{
  “compilerOptions”: {
    “strictNullChecks”: false,
    “target”: “es2022”
  }
}

同时,你可能还需要创建一个 tsconfig.json 文件,其配置示例如下:

{
  // 根据你的项目结构调整 `include` 路径
  “include”: [“src/**/*”],
  “compilerOptions”: {
    // 允许读取 JS 文件,默认会忽略它们
    “allowJs”: true,
    // 生成 .d.ts 声明文件
    “declaration”: true,
    // 仅生成声明文件,不输出 JS
    “emitDeclarationOnly”: true,
    // 声明文件输出目录
    “outDir”: “types”,
    // 在编辑器中“转到定义”时,跳转到 JS 源文件
    “declarationMap”: true
  }
}

我知道初看之下配置项有点多,但请放心,一旦你的编辑器和命令行工具配置妥当,这套流程可以轻松复用到无数项目中,你会很快上手。

理论不如实践,让我们直接看看具体的代码示例。

JSDoc 实战

以下是一个包含多个参数的类构造函数的注释示例:

class ReciprocalProperty {
  /**
   * @param {HTMLElement} element - 要绑定的 DOM 元素
   * @param {string} name - 属性名称
   * @param {(value: any) => any} signalFunction - 用于创建信号的函数
   * @param {() => any} effectFunction - 用于建立副作用的函数
   */
  constructor(element, name, signalFunction, effectFunction) {
    this.element = element
    this.name = name
    this.type = this.determineType()
    // 其他初始化逻辑...
  }
}

如你所见,当你对变量的具体类型不那么关心时,使用 any 类型完全没有问题。将类型信息写入注释的一大额外好处是:你不仅获得了类型提示,还顺便完成了代码文档的编写!

再比如,声明一个对象类型(在 TypeScript 中常称为 Record):

/** @type {Record<string, ReciprocalProperty>} */
const attrProps = this.element[“_reciprocalProperties”]

这里使用 this.element[“_reciprocalProperties”] 而非点号语法,是因为 TypeScript 对访问对象上未声明的属性会发出警告。使用方括号表示法可以避免这个错误(前提是你确信这个属性存在)。

下面是在 for…of 循环中内联声明类型的例子:

for (const stop of /** @type {StreetcarStatementElement[]} */ ([…this.children])) {
  stop.operate()
}

这种语法确实需要一点时间来适应。通常的规则是,当你需要在一段表达式前进行内联类型声明时,可以将 /** @type */ 注释放在一个用括号包裹的代码段之前,这通常都能奏效。

另一个例子是在函数参数中进行内联类型声明:

htmx.defineExtension(“streetcar”, {
  handleSwap: (swapStyle, _target, /** @type {Element} */ fragment) => {
    // 处理逻辑...
  },
})

你还可以通过 @typedef 语法来“导入”仅用于类型的定义(注意,这不是普通的 JavaScript 导入):

/**
 * @typedef { import(“./HostEffects.js”).default } HostEffects
 */

// 随后即可使用:
/**
 * @param {HostEffects} effects
 */
function setup(effects) { }

@typedef 也可用于定义相当于 TypeScript 中 interface 的结构,JSDoc 官方文档对此有详细说明。

最后,有时你可能会编写一些 TypeScript 无法理解或报错的代码,这也没关系。正如官方文档所说:

当你认为 TypeScript 给出的错误不合理时,可以在该行代码前一行添加 // @ts-ignore// @ts-expect-error 来忽略该特定错误。

为何 JavaScript + JSDoc + tsc 是更佳组合

我曾多次表达过一个观点:将 TypeScript 作为行业默认选择,可能是一个不小的误区。我真诚地希望更多开发者能开始编写真正符合开放 Web 标准的 .js 文件——这些文件无需任何构建步骤或转译工具,就能在浏览器或 Node.js 环境中直接运行。

与此同时,通过结合使用 JSDoc 和 tsc,你可以在 IDE 中获得智能的类型提示,在持续集成流水线中进行严格的类型检查,从而享受到与使用 TypeScript 几乎相同的开发体验。这种方式真正实现了“鱼与熊掌兼得”。而真正必须使用 .ts 文件的场景,在实践中少之又少。

当然,某些前端框架或工具链可能“强制”要求使用 TypeScript 编写代码。如果遇到这种情况,遵循框架约定即可。
但如果你对项目的技术选型拥有决策权,我强烈建议你考虑使用纯粹的 JavaScript

毕竟,ECMAScript (JavaScript) 才是 Web 世界的通用语言,而非任何其超集或变体。通过在代码中编写清晰的 JSDoc 注释并利用 TypeScript 编译器进行静态验证,你可以在保持代码纯净、可移植性的同时,获得强大的类型安全保障。

希望这篇实践指南能为你提供一种新的视角。欢迎在 云栈社区 分享你在实际项目中使用 JSDoc 和类型检查的心得与技巧。

关于本文




上一篇:深度解析RocketMQ拉模式消费者DefaultLitePullConsumer的核心原理与应用实践
下一篇:展望2026:COBOL、C语言、Linux等历久弥坚的技术栈盘点
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-1-10 08:51 , Processed in 0.299472 second(s), 40 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2025 云栈社区.

快速回复 返回顶部 返回列表