JSDoc 真能取代 TypeScript?

这几个月,想必大家都听到过一个新闻:

Svelte 弃用 TypeScript,改用 JSDoc 了。

TypeScript 我们知道,是用来给 JS 加上类型的,可以实现类型提示和编译时的类型检查。

那 JSDoc 能够完成一样的功能么?Svelte 是出于什么原因弃用 TS 的呢?

先不着急回答这个问题。

我们总得先了解下 JSDoc:

可能大家认为的 JSDoc 是这个东西:

在代码的注释上加上类型的标识,然后通过 jsdoc 命令行工具,就可以直接生成文档。

比如这样的文档:

确实,这个是 JSDoc 最初的含义。

但我们说的 JSDoc 并不是这个,而是 TS 基于 JSDoc 语法实现的,在注释里给代码添加类型的语法。

文档在这里

ts 支持在 js 文件的注释里,通过 JSDoc 的语法给它加上类型。

至于有什么意义,那可就太多了。

比如一个 JS 的配置文件,你想在写配置的时候能有提示,就可以用 JSDoc:

这里注释里的 @type 就是 JSDoc 声明类型的语法。

在 vite 文档里,你可以看到对 JSDoc 的支持:

我们自己试一下:

bash 复制代码
mkdir jsdoc-test
cd jsdoc-test
npm init -y

创建项目和 package.json。

然后安装 typescript:

css 复制代码
npm install --save-dev typescript

创建 tsconfig.json 文件:

csharp 复制代码
npx tsc --init

生成的 tscconfig.json 太多注释了,我们删一下:

然后创建 src/index.ts

javascript 复制代码
function add(a: number, b: number) {
    return a + b;
}

这样在用到这个 add 的时候,就会做类型检查:

在 tsconfig.json 里 include 一下:

之后执行编译:

npx tsc

生成的代码是这样的:

这个是 ts 的编译流程,大家都很熟悉。

现在问题来了,我有一个 src/index2.js,怎么实现一样的类型检查呢?

这样写:

javascript 复制代码
/**
 * @param {number} a  参数aaa
 * @param {number} b  参数bbb
 */
function add2(a, b) {
    return a + b;
}

注释里的就是 JSDoc 的语法。

但现在并没有报类型错误:

需要在 tsconfig 里开启:

allowJS 是允许编译 JS,checkJS 是在编译 JS 的时候也做类型检查。

开启后你就会发现,js 文件里也会做类型检查了:

hover 上去的时候,会提示类型信息:

注意,这可不是用 ts 语法声明的类型,而是用 JSDoc 写的。

然后我们开启 dts:

再编译:

npx tsc

可以看到同样能产出 d.ts 类型声明文件:

而这时候产物的 JS 代码和源码差别不大:

因为本来 JSDoc 就是在注释里的,类型检查也好、生成 dts 也好,都不用改动源码。

这就是 JSDoc 最大的好处:无侵入的给 JS 加上类型,拥有和 ts 一样的类型检查、类型提示、生成 dts 等功能,但却不需要编译,因为 JS 代码可以直接跑。

有同学可能会说,就声明个函数类型就和 ts 一样了?

那肯定不止这么点语法,我们再看几个:

比如可以用 @type 给变量声明类型:

这里可以是各种类型,比如函数类型:

如果类型被多处用到,可以用 @typedef 抽出来,单独命名:

你还可以把这个类型放到 dts 文件里,在这里 import 进来用:

比如我把它放到 guang.d.ts 里:

然后这样引入用:

这样就避免了在 @typedef 里写大段类型定义了,因为那里也不能换行,代码写多了就不好维护了。

这样就可以在 dts 里定义类型,然后在 js 里通过 JSDoc 引入来用。

dts + JSDoc 是绝佳搭配。

然后我们继续看 JSDoc 的函数类型定义:

这分别是可选参数、参数默认值、返回值类型的语法。

还有同学说,那 ts 的泛型呢?这个 JSDoc 不支持的吧?

当然也是支持的,这样写:

通过 @template 声明类型参数,然后下面就可以用了。

泛型都可以用,那基于泛型的类型编程,也就是类型体操当然也可以玩:

一般这种复杂类型还是抽离到 dts 里,然后 @type {import('xxx').xxx} 引入比较好。

再就是 class 了,这个自然也是支持的。

比如声明一个泛型类:

这段类型大家能看懂么?

就是声明了一个泛型类,有一个类型参数 T。它通过 @extends 继承了 Set<T> 类型。

它有个 name 属性的类型为 T,并且还声明了构造器和 sleep 方法的类型。

用一下试试:

name 和 sleep 的类型,继承的 Set 的类型,都没问题。

这就是 JSDoc 定义 class 类型的方式。

综上,用 JSDoc 可以定义变量、函数、class、泛型等,可以从别的 dts 文件引入类型。

基本上 ts 能做的,JSDoc 也都可以。

但是,JSDoc 语法毕竟是在注释里的,多了一大坨东西,而且写起来也不如 ts 语法直观。

所以,一般没必要这样写,除非你是给 JS 加类型。

那 svelte 是出于什么原因选择了 JSDoc 的方式呢?

看下那个 pr 就知道了:

直接看官方回复:

也就是说,用 ts 的语法,需要编译后才能调试,这样需要再 sourcemap 一次才能对应到源码。

但是用 JSDoc 的方式,不用编译就可以直接调试。

估计是遇到了啥 VSCode 调试上的问题。

然后下面还有个 VSCode 调试器的维护者评论说,有任何调试相关的问题可以找我:

总之,svelte 选择从 ts 转成 JSDoc + dts 并不是因为它有啥问题,主要是为了调试方便。

那我们再看下它怎么用的吧:

可以看到,是 js 文件里用 JSDoc 来声明类型:

然后复杂类型在 dts 里定义,然后这里引入:

就和我们刚才测试的一样。

总结

这几个月经常听到知名开源项目抛弃 ts 拥抱 JSDoc 的新闻,我们一起探究了一下。

JSDoc 是在 js 的注释里通过 @type、@typedef、@template、@param 等来定义类型,然后开启 checkJS 和 allowJS 的配置之后,tsc 就可以对 js 做类型检查。

ts 里可以定义的类型,在 JSDoc 里都可以定义,比如变量、函数、class、泛型,甚至类型编程等。复杂的类型还可以抽离到 dts 里,在 @type 里引入。

也就是说 JSDoc 确实可以替代 ts。

然后我们看了 svelte 选择 JSDoc 的原因,只是为了调试方便。这样不用编译就可以直接跑 js 代码,可以直接定位到源码。而且这样也能利用 ts 的类型提示和检查的能力。

所以很多人就说 svelte 抛弃了 ts。

这叫抛弃么?

并不是,JSDoc 只是另一种使用 ts 的方式而已。

相关推荐
Martin -Tang33 分钟前
vite和webpack的区别
前端·webpack·node.js·vite
迷途小码农零零发34 分钟前
解锁微前端的优秀库
前端
王解1 小时前
webpack loader全解析,从入门到精通(10)
前端·webpack·node.js
老码沉思录1 小时前
写给初学者的React Native 全栈开发实战班
javascript·react native·react.js
我不当帕鲁谁当帕鲁2 小时前
arcgis for js实现FeatureLayer图层弹窗展示所有field字段
前端·javascript·arcgis
那一抹阳光多灿烂2 小时前
工程化实战内功修炼测试题
前端·javascript
放逐者-保持本心,方可放逐2 小时前
微信小程序=》基础=》常见问题=》性能总结
前端·微信小程序·小程序·前端框架
毋若成5 小时前
前端三大组件之CSS,三大选择器,游戏网页仿写
前端·css
红中马喽5 小时前
JS学习日记(webAPI—DOM)
开发语言·前端·javascript·笔记·vscode·学习
Black蜡笔小新6 小时前
网页直播/点播播放器EasyPlayer.js播放器OffscreenCanvas这个特性是否需要特殊的环境和硬件支持
前端·javascript·html