无需编译即可使用 Typescript

2025-05-28

无需编译即可使用 Typescript

过去几天,一篇关于 Svelte 下一个主要版本的文章在 Twitter 上爆红,引发了广泛讨论。文章指出:

该团队正在将底层代码从 TypeScript 转换为 JavaScript。

公平地说,这有点误导。从技术角度来看,这篇文章并没有错,团队只是将底层代码从 TypeScript 切换到 JavaScript。然而,他们并没有放弃代码中的类型检查。他们只是从直接编写 TypeScript 源代码转变为结合使用 JSDoc 和.d.ts文件编写 JavaScript 源代码。这使得他们能够:

  • 编写类型安全的代码,使用 TypeScript 进行类型检查
  • 编写并发布纯 JavaScript
  • 跳过 TypeScript 的编译步骤
  • 仍然为最终用户提供类型

这场讨论的有趣之处在于,很多人对此感到非常不满,Twitter 上也掀起了关于类型检查的讨论。我们看到,当 ESLint 团队宣布他们不想使用 TypeScript 重写 ESLint 时,也发生了同样的事情,而是选择了与 Svelte 团队相同的方法:使用纯 JavaScript 和 JSDoc 进行类型检查。在这些 Twitter 讨论中,很明显,很多人,甚至一些自称“教育者”的人,并不了解 JSDoc 的实际功能,不幸的是,他们只会散布关于这种工作方式的赤裸裸的谎言。

这里需要注意的是,这两个团队都没有忽视类型安全。他们只是选择了一种不同的类型安全利用方式,即省去了编译步骤。这是一种偏好。没有正确或错误的答案;无论哪种方式,使用 TypeScript 都可以获得类型安全。本次讨论和这些决定与使用 TypeScript 无关。除非您直接参与或参与其中任何一个项目,否则这些决定与您无关。坦率地说,这不关您的事。如果想在编译步骤中使用 TypeScript,那就去做吧!没有必要抱有敌意。这些项目仍然使用 TypeScript 来确保其代码的类型安全,并且仍然可以将类型交付给最终用户。在这篇博客中,我们将探讨跳过 TypeScript 编译步骤的好处,澄清我看到的一些流传甚广的误解,并强调理解和尊重不同偏好和方法的重要性。

在这篇博客中,我不会详细介绍如何设置你的项目以启用 JSDoc 的类型检查,有很多很棒的资源可以参考,比如这里

在深入探讨之前,我想再次重申,通过 JSDoc 使用类型,人们仍然可以使用 TypeScript 的类型检查器编写类型安全的 JavaScript。它只是跳过了编译步骤。您还可以.d.ts在必要时(但并非必须!)使用文件,并为用户提供类型。是的,这种方法仍然在使用 TypeScript

跳过编译步骤的好处

JavaScript 工具生态系统中的编译或转译步骤通常有些“必要之恶”,例如转译 JSX 代码,或者在本例中是 TypeScript 代码。它们通常不如我们所期望的那么快,并且通常需要一些配置调整(尽管需要注意的是,近年来许多工具已经得到了改进)才能使您的设置正常工作。这不仅是为了构建生产项目,也是为了为您的本地开发环境以及测试运行器正确设置所有内容。虽然编译或转译提供了便利(编写 JSX 源代码,而不是手动调用 React.createElement,或者直接在源代码中编写类型),但有些人认为这些编译步骤并不可取,并且倾向于尽可能地跳过它们。

在 TypeScript 的使用中,跳过编译步骤有几个好处。它使你的代码运行时不可知;你的代码可以在 Node、Deno、浏览器、类似 Worker 的环境中运行。其中一些环境,例如 Deno,原生支持运行 TypeScript(这会带来一系列令人担忧的影响*)。而另一些环境,例如浏览器,则不支持(至少在“类型作为注释”提案发布之前不支持)。根据你正在构建的内容,这对你来说可能是个问题,也可能不是,但同样,有些人认为这是更好的选择。

  • 有人指出,Deno 现在--no-check默认会使用 运行,这缓解了一些问题。但是,使用 时问题仍然存在--check

如果您的代码与运行时无关,它还允许您轻松地将代码片段复制并粘贴到 REPL 中。发布原生 JavaScript 还可以简化调试,请考虑以下示例:您已将软件包作为原生 JavaScript 发布。有人安装了您的软件包并发现了一个错误。他们可以直接进入他们的 node_modules 并轻松调整一些代码来尝试修复该错误,而无需处理转译代码、源映射等。

我个人发现使用 JSDoc 的另一个好处(这只是个人偏好)是,与 TypeScript 相比,编写代码文档的门槛要低得多。请考虑以下示例:

创建一个简单的原始“add” endraw 函数,当输入原始“/**” endraw 时,代码编辑器会自动完成类型的脚手架,并将文档添加到函数中

不可否认,名为“可能”的函数add不需要大量文档,但出于说明目的。

当我敲击/**<ENTER>键盘时,编辑器已经为我搭建好了 JSDoc 注释的脚手架,我只需要编写类型即可。注意,返回类型可以省略,因为 TypeScript 仍然会根据代码正确地推断出返回类型。虽然我已经有 JSDoc 注释了,但我还是想为它添加一些文档!非常简单。

神话

使用 JSDoc 是难以维护的

Twitter 上有些人对使用 JSDoc 来声明类型的可维护性表示担忧,并声称 JSDoc 只适用于小型项目。作为一个在工作中维护过许多通过 JSDoc 使用类型的项目(其中一些项目规模很大)的人,我可以告诉你,这种说法完全是错误的。确实,如果只使用 JSDoc 来声明和使用类型,有时会变得有点笨拙。但是,为了避免这种情况,你可以将 JSDoc 与.d.ts文件结合使用。在文件中声明你的类型.d.ts

./types.d.ts



export interface User {
  username: string,
  age: number
}


Enter fullscreen mode Exit fullscreen mode

并将其导入到您的源代码中
./my-function.js



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

/**
 * @param {User}
 */
function foo(user) {}


Enter fullscreen mode Exit fullscreen mode

没有类型推断或智能感知

有些人似乎认为使用 JSDoc 会导致类型推断失效。正如上面已经证明的那样,这同样是错误的。请考虑以下示例:

原始“add” endraw 函数的返回类型被正确推断

这种说法的原因似乎是人们不明白,当你使用 JSDoc 来定义类型时,你仍然在使用 typescript。TypeScript仍然在进行typechecking

手动编写类型很麻烦

有些人抱怨手动编写类型很麻烦。我只能假设这只是个人偏好问题,或者这些人可能不清楚仍然可以使用.d.ts文件。有些人更喜欢example B而不是example A。这没关系。在使用 JSDoc 描述类型时,两者都可以使用。

example A



/**
 * @typedef {Object} User
 * @property {string} username
 * @property {number} age
 */


Enter fullscreen mode Exit fullscreen mode

example B



export interface User {
username: string,
age: number
}

Enter fullscreen mode Exit fullscreen mode




但这仍然使用 TypeScript!

是的,这就是重点。

综上所述

最后,我再重复一遍,使用 TypeScript 而不编译代码是一种选择。这没有对错之分,我建议任何对此方法持怀疑态度的人,在开始新项目时,不妨尝试一下,你可能会发现这实际上是一种非常不错的类型利用方法。如果你最终不喜欢它,也没关系!

文章来源:https://dev.to/thepassle/using-typescript-without-compilation-3ko4
PREV
欢迎主题 - v36
NEXT
回归基础:我们应该使用 Flexbox 还是 Grid?