还没用熟 TypeScript 社区已经开始抛弃了😭

190,821 阅读4分钟

根据 rich-harris-talks-sveltekit-and-whats-next-for-svelte 这篇文章的报道, Svelte 计划要把代码从 TS 换到 JS 了。

The team is switching the underlying code from TypeScript to JavaScript. That and the update will then allow the team to incorporate “big ideas” for Svelte 5 later this year, he added.

这种震惊劲爆的信息,当然的核实下是不是准确的,于是去 svelte 框架的作者 Rich Harris 的推特去求证下,好奇的不止我一个,已经有人提问了,并且作者给出了答案,非常确定

最新消息,非常确定了,就在昨天,北京时间 2022 年 5 月 9 日,Svelte 团队发布了一个名为 TS to JSDoc Conversion 的 PR,开始这项浩瀚的工程,同时宣布目前 Svelte 不再支持重大更新了,根据点不支持的人数,看到这件事情比较有争议,不支持的人不在少数。

TS to JSDoc Conversion

事实上,这不是社区第一次放弃 TypeScript 了,比如 Deno 远在 2020 年就弃用了 TS,并给出三大理由:

  1. 减少了构建时间
  2. 发布代码变小了
  3. 写的代码大大减少了

那个时候 TyepScript 的发展正在如日中天的时候,广大库的作者普遍拥抱 TS,比如于2020年9月18日正式发布的Vue3 ,代号为 One Piece(海贼王)。

三年过去了,再好看的媳妇也看腻了,大家就开始挑毛病了,你(TyepScript)可能并不完美。

回归了理性,大家就开始思考使用 TyepScript 的初心是什么了,意识吼出了灵魂一问?我们为什么使用 TypeScript?

没错,这个问题很简单,因为 TypeScript 提供了类型检查,弥补了 JavaScript 只有逻辑没有类型的问题,也就是讲我们不需要 TypeScript 的逻辑,只需要它的的类型提示功能。但是不知不觉之间,我们在逻辑的道路上越走越远。

比如下面是 Vue3 watch API 的类型声明,我估计给一天时间,大多数人可能都不太能整的明白里面的逻辑:

export declare function watch<T extends MultiWatchSources, Immediate extends Readonly<boolean> = false>(sources: [...T], cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>, options?: WatchOptions<Immediate>): WatchStopHandle;

export declare function watch<T extends Readonly<MultiWatchSources>, Immediate extends Readonly<boolean> = false>(source: T, cb: WatchCallback<MapSources<T, false>, MapSources<T, Immediate>>, options?: WatchOptions<Immediate>): WatchStopHandle;

export declare function watch<T, Immediate extends Readonly<boolean> = false>(source: WatchSource<T>, cb: WatchCallback<T, Immediate extends true ? T | undefined : T>, options?: WatchOptions<Immediate>): WatchStopHandle;

export declare function watch<T extends object, Immediate extends Readonly<boolean> = false>(source: T, cb: WatchCallback<T, Immediate extends true ? T | undefined : T>, options?: WatchOptions<Immediate>): WatchStopHandle;

而且如果项目引用了用 TypeScript 编写的库,需要频繁借助 VSCode 等编辑器查看源代码,才能进行类型声明继续编写逻辑代码。

以前我也是 TypeScript 的拥趸,但是使用了一两年之后,我改变了看法,平时应付业务逻辑已经够费脑子了,现在需要花不少时间去调整代码来适应需求。

除此之外,dev 开发代码进行类型检查也比较费时间,项目大了可能需要顿个几秒才能检查完成,再进行代码编译输出到浏览器。

现在明白了最初的需求,完全可以用 JavaScript + JSDoc 来解决类型声明,现代编辑器是认的 JSDoc,友好支持程度一点不比 TS 差,如果是编写库,需要导出给安装者使用,那就在 .d.ts 文件中定义导出给使用者。

使用 JSDoc 表达类型,不仅省去了构建步骤,不打包都可以直接用,还可以避免编写复杂的类型逻辑,太方便了有没有,代码可以复制到任何 JS 的运行环境心动没有。

我们来实践看看行不行的通,光说不练,假把式。

这里以 Svgo 的一个函数 removeLeadingZero 为例,这个函数可以删除小数的前导零并作为字符串返回,比如 0.5 → .5-0.5 → -.5

const removeLeadingZero = (num) => {
	let strNum = num.toString();

	if (0 < num && num < 1) {
		strNum = strNum.slice(1);
	} else if (-1 < num && num < 0) {
		strNum = "-" + strNum.slice(2);
	}
	
	return strNum;
};

我们添加如何注释:

/**
 * Remove floating-point numbers leading zero.
 *
 * @example
 * 0.5 → .5
 *
 * @example
 * -0.5 → -.5
 *
 * @type {(num: number) => string}
 */

非常好用:

image.png

实际上 VSCode 有智能推断,简单的代码都能推断出来,比如,const num = 23; 会自动感应出来方法:

image.png

说回来 removeLeadingZero 函数,当我们调用的时候,传入错误的参数,没有像 TS 类型强制报错:

image.png

解决办法也很简单,比如:

  1. 利用的 TS @ts-check 注释
  2. 添加 tsconf.json / jsconfig.json 并让 checkJs 为 true。
{
  "compilerOptions": {
    "checkJs": true
  },
  "exclude": ["node_modules", "**/node_modules/*"]
}
  1. 添加"js/ts.implicitProjectConfig.checkJs": true到您的工作区或用户设置里面即 settings.json 文件。
  2. 更多参考 type-checking-javascript

改正后的效果:

image.png

事实上 JSDoc 的类型注释非常丰富比如还有 @param@const 等等,但不复杂,学习成本很低。

参考