Mind Elixir 在 2021 年 10 月开始逐渐迁移到 TypeScript,很忏愧地说直到最近,我才狠下心来打开了把 compilerOptions.strict
设成了 true
。
那时不太懂 TS,跌跌撞撞地写类型,到现在算是积累了点经验,而且日常工作中的项目也已经用上了 TS。所以大概是时候了,正式把整个项目变成 TS 项目,并且支持 TS 文件引入时获得类型提示。
讲讲这迁移路上的一些事吧,不得不说 vanilla JavaScript 项目迁移到 TS 是真的简单,基本上加个 loader(如果你的 bundler 适配的话甚至不用加),加 tsconfig.json
,重点基本就下面两个选项:
配置完成后,把 .js
文件改为 .ts
文件,接着开始着手添加类型就完事了。
在慢慢加类型的时候第一个问题来了,我写的全局自定义类型要放哪里呢?如果现在向我提问的话果然直接还是走 ESM 算了,放在普通 ts
文件,export
它,然后 import type
。虽然当时也知道有这个办法,但是觉得全部类型全都 export import 实在太烦了,明明就是一个库,为什么他不能直接写在一个文件夹里由 IDE 自动感应就好了呢?
大概基于这个想法,我老是在想 d.ts
,其实 2021 年的主流也不至于还是 d.ts
,但我还是选了,现在看来感觉这步我是走错了。
不仅错了,而且这步其实也不太好走,我想方设法放置我的全局变量,发现有时候总是检测不了,至于当时怎么写的我也忘了,就不提了,反正也没什么值得注意的地方。后来,我遇到了这篇文章 A quick introduction to “Type Declaration” files and adding type support to your JavaScript packages,然后我惊呼:真不错!
我一下就抓住了救命稻草 /// <reference path="./xxx.d.ts" />
。
这个方案需要在 tsconfig.json
设置 typeRoots
。默认情况下,TS 会在 node_modules/@types
目录中查找类型定义文件,但是显然不是每一个库都提供了类型渲染,于是你可以添加 typeRoots
,然后 TS 就会在 typeRoots
里找。
在刚才设置的 types
文件夹里多加个 common
文件夹区分不同包,还可以通过 package.json
的 typings
配置入口文件。
之后你无论把类型分多少个文件,只要在入口文件添加 /// <reference path="./xxx.d.ts" />
就能引入这个文件,不需要 import,一切都是那么的自然。这个方案挺好的,很方便,随时加 type,全局可用,但是……
Mind Elixir 本身就是要给别人用的,打包之后,使用者要得到类型提示可要咋办?项目本身 TS 开发,但是引入的时候把 TS 丢了,确实挺怪的。
而这个问题的答案,我现在还在思考。
按照 typeRoots
的方案,用户确实可以以同样方法引入我提供的类型,但是我推测这种方法仅仅能拿到我定义的类型,但是实际 import 的时候并不会自动推理出 import 的类型,这也十分致命。
之后我将目光投向 compilerOptions
里输出相关的选项:
这么配置之后,tsc
时会自动帮你按源文件结构输出一份 d.ts
,然后在 package.json
里说明你的 type 放哪就行了。这样,用户在 import 时会自动识别出引入内容的类型。
然而事情没有这么容易被解决,之前也提到 d.ts
按源文件结构被输出,换言之,如果类型没有被 export 的话,生成的 d.ts
里根本就不存在那些类型,结果就是一大堆类型缺失了,IDE 只会把他们当 any
。
所以目前我的路就只有两条了,要不老实用回 import type
那条我从一开始就没选择的路,要不再想想有什么办法能自动生成没有引入的类型,不过这条路是希望渺茫啦,所以……
还是免不了啪啪打自己的脸,慢慢改回 import type
吧,然后再下一步就是吧 allowJs
设为 false
,迁移就完工了,提前撒花 ★,°:.☆( ̄ ▽  ̄)/$:.°★ 。
P.S. 最后还是得说一句,TS 最最最最最最最最最不友好的地方自然,是对构造函数的支持,真的烦死,逼人用 class,但是真的不想换,最后坚持普通构造函数加一些歪门邪道解决问题