最近的一段时间一直在搞TypeScript
,一个巨硬出品、赋予JavaScript
语言静态类型和编译的语言。
第一个彻底使用TypeScript
重构的纯Node.js
项目已经上线并稳定运行了。
第二个先后端的项目目前也在重构中,关于前端基于webpack
的TypeScript
套路以前也有提到过:TypeScript在react项目中的实践。 前端
可是这些作完之后也总感受缺了点儿什么 _(没有尽兴)_:node
是的,依然有五分之一的JavaScript
代码存在于项目中,做为一个TypeScript
的示例项目,表现的很不纯粹。
因此有没有可能将这些JavaScript
代码也换成TypeScript
呢?
答案确定是有的,首先须要分析这些代码都是什么:react
Webpack
打包时的配置文件知道了是哪些地方还在使用JavaScript
,这件事儿就变得很好解决了,从构建工具(Webpack
)开始,逐个击破,将这些所有替换为TypeScript
。webpack
在这8102
年,很幸福,Webpack
官方已经支持了TypeScript
编写配置文件,文档地址。
除了TypeScript
之外还支持JSX
和CoffeeScript
的解释器,在这就忽略它们的存在了git
首先是要安装TypeScript
相关的一套各类依赖,包括解释器及该语言的核心模块:github
npm install -D typescript ts-node
typescript
为这个语言的核心模块,ts-node
用于直接执行.ts
文件,而不须要像tsc
那样会编译输出.js
文件。web
ts-node helloworld.ts
由于要在TypeScript
环境下使用Webpack
相关的东东,因此要安装对应的types
。
也就是Webpack
所对应的那些*.d.ts
,用来告诉TypeScript
这是个什么对象,提供什么方法。typescript
npm i -D @types/webpack
一些经常使用的pLugin
都会有对应的@types
文件,能够简单的经过npm info @types/XXX
来检查是否存在 npm
若是是一些小众的plugin
,则可能须要本身建立对应的d.ts
文件,例如咱们一直在用的qiniu-webpack-plugin
,这个就没有对应的@types
包的,因此就本身建立一个空文件来告诉TypeScript
这是个啥:json
declare module 'qiniu-webpack-plugin' // 就一个简单的定义便可 // 若是还有其余的包,直接放到同一个文件就好了 // 文件名也没有要求,保证是 d.ts 结尾便可
放置的位置没有什么限制,随便丢,通常建议放到types
文件夹下
最后就是.ts
文件在执行时的一些配置文件设置。
用来执行Webpack
的.ts
文件对tsconfig.json
有一些小小的要求。 compilerOptions
下的target
选项必须是es5
,这个表明着输出的格式。
以及module
要求选择commonjs
。
{ "compilerOptions": { "module": "commonjs", "target": "es5", "esModuleInterop": true } }
但通常来说,执行Webpack
的同级目录都已经存在了tsconfig.json
,用于实际的前端代码编译,极可能两个配置文件的参数并不同。
若是由于要使用Webpack
去修改真正的代码配置参数确定是不可取的。
因此咱们就会用到这么一个包,用来改变ts-node
执行时所依赖的配置文件:tsconfig-paths
在Readme
中发现了这样的说法:If process.env.TS_NODE_PROJECT is set it will be used to resolved tsconfig.json
。
在Webpack
的文档中一样也提到了这句,因此这是一个兼容的方法,在命令运行时指定一个路径,在不影响原有配置的状况下建立一个供Webpack
打包时使用的配置。
Webpack
文档示例中为tsconfig-for-webpack-config.json
,这里就直接沿用了npm script
以下{ "scripts": { "build": "TS_NODE_PROJECT=tsconfig-for-webpack-config.json webpack --config configs.ts" } }
关于配置文件,从JavaScript
切换到TypeScript
实际上并不会有太大的改动,由于Webpack
的配置文件大多都是写死的文本/常量。
不少类型都是自动生成的,基本能够不用手动指定,一个简单的示例:
import { Configuration } from 'webpack' const config: Configuration = { mode: process.env.NODE_ENV === 'production' ? 'production' : 'development', } export default config
Configuration
是一个Webpack
定义的接口(interface
),用来规范一个对象的行为。
在VS Code
下按住Command
+ 单击能够直接跳转到具体的webpack.d.ts
定义文件那里,能够看到详细的定义信息。
各类经常使用的规则都写在了这里,使用TypeScript
的一个好处就是,当要实现一个功能时你再也不须要去网站上查询应该要配置什么,能够直接翻看d.ts
的定义。
若是注释写得足够完善,基本能够当成文档来用了,并且在VS Code
编辑器中还有动态的提示,以及一些错误的纠正,好比上述的NODE_ENV
的获取,若是直接写process.env.NODE_ENV || 'development'
是会抛出一个异常的,由于从d.ts
中能够看到,关于mode
只有三个有效值production
、developemnt
和none
,而process.env.NODE_ENV
显然只是一个字符串类型的变量。
因此咱们须要使用三元运算符保证传入的参数必定是咱们想要的。
以及在编写的过程当中,若是有一些自定义的plugin
之类的,可能在使用的过程当中会抛异常提示说某个对象不是有效的Plugin
对象,一个很简单的方法,在对应的plugin
后边添加一个as webpack.Plugin
便可。
在这里TypeScript
所作的只是静态的检查,并不会对实际的代码执行形成任何影响,就算类型由于强行as
而改变,也只是编译期的修改,在实际执行的JavaScript
代码中仍是弱类型的
在完成了上述的操做后,再执行npm run XXX
就能够直接运行TypeScript
版本的Webpack
配置咯。
由于个人项目根目录已经安装了ts-node
,而前端项目是做为其中的一个文件夹存在的,因此就没有再次进行安装。
这就带来了一个使人吐血的问题。
首先所有流程走完之后,我直接在命令行中输入TS_NODE_PROJECT=XXX.json NODE_ENV=dev webpack --config ./webpack/dev.ts
完美运行,而后将这行命令放到了npm scripts
中:
{ "scripts": { "start": "TS_NODE_PROJECT=XXX.json NODE_ENV=dev webpack --config ./webpack/dev.ts" } }
再次运行npm start
,发现居然出错了-.-,提示我说import
语法不能被识别,这个很显然就是没有应用咱们在ts_NODE_PROJECT
中指定的config
文件。
刚开始并不知道问题出在哪,由于这个在命令行中直接执行并无任何问题。
期间曾经怀疑是不是环境变量没有被正确设置,还使用了cross-env
这个插件,甚至将命令写到了一个sh
文件中进行执行。
然而问题依然存在,后来在一个群中跟小伙伴们聊起了这个问题,有人提出,__你是否是全局安装了ts-node
__。
检查之后发现,果真是的,在命令行执行时使用的是全局的ts-node
,可是在npm scripts
中使用的是本地的ts-node
。
在命令行环境执行时还觉得是会自动寻找父文件夹node_modules
下边的依赖,实际上是使用的全局包。
乖乖的在client-src
文件夹下也安装了ts-node
就解决了这个问题。
全局依赖害人。。
前边的Webpack
改成TypeScript
大多数缘由是由于强迫症所致。
可是测试用例的TypeScript
改造则是一个能极大提升效率的操做。
测试用例使用chai
来编写,_(以前的Postman
也是用的chai
的语法)_ chai
提供了一系列的语义化链式调用来实现断言。
在以前的分享中也提到过,这么多的命令你并不须要彻底记住,只知道一个expect(XXX).to.equal(true)
就够了。
可是这样的通篇to.equal(true)
是巨丑无比的,而若是使用那些语义化的链式调用,在不熟练的状况下很容易就会获得:
Error: XXX.XXX is not a function
由于这确实有一个门槛问题,必需要写不少才能记住调用规则,各类not
、includes
的操做。
可是接入了TypeScript
之后,这些问题都迎刃而解了。
也是前边提到的,全部的TypeScript
模块都有其对应的.d.ts
文件,用来告诉咱们这个模块是作什么的,提供了什么可使用。
也就是说在测试用例编写时,咱们能够经过动态提示来快速的书写断言,而不须要结合着文档去进行“翻译”。
若是是以前有写过mocha
和chai
的童鞋,基本上修改文件后缀+安装对应的@types
便可。
能够直接跳到这里来:开始编写测试脚本
可是若是对测试用例感兴趣,可是并无使用过的童鞋,能够看下边的一个基本步骤。
TypeScript
相关的安装,npm i -D typescript ts-node
Mocha
、chai
相关的安装,npm i -D mocha chai @types/mocha @types/chai
chai-http
,npm i -D chai-http @types/chai-http
环境的依赖就已经完成了,若是额外的使用一些其余的插件,记得安装对应的@types
文件便可。
若是有使用ESLint之类的插件,可能会提示modules
必须存在于dependencies
而非devDependencies
这是ESLint的import/no-extraneous-dependencies
规则致使的,针对这个,咱们目前的方案是添加一些例外:
import/no-extraneous-dependencies: - 2 - devDependencies: - "**/*.test.js" - "**/*.spec.js" - "**/webpack*" - "**/webpack/*"
针对这些目录下的文件/文件夹不进行校验。_是的,webpack的使用也会遇到这个问题_
若是是对原有的测试脚本进行修改,无外乎修改后缀、添加一些必要的类型声明,不会对逻辑形成任何修改。
// number-comma.ts export default (num: number | string) => String(num).replace(/\B(?=(\d{3})+$)/g, ',') // number-comma.spec.ts import chai from 'chai' import numberComma from './number-comma' const { expect } = chai // 测试项 describe('number-comma', () => { // 子项目1 it('`1234567` should transform to `1,234,567`', done => { expect(numberComma(1234567)).to.equal('1,234,567') done() }) // 子项目2 it('`123` should never transform', done => { const num = 123 expect(numberComma(num)).to.equal(String(num)) done() }) })
若是全局没有安装mocha
,记得将命令写到npm script
中,或者经过下述方式执行
./node_modules/mocha/bin/mocha -r ts-node/register test/number-comma.spec.ts # 若是直接这样写,会抛出异常提示 mocha 不是命令 mocha -r ts-node/register test/number-comma.spec.ts
mocha
有一点儿比较好的是提供了-r
命令来让你手动指定执行测试用例脚本所使用的解释器,这里直接设置为ts-node
的路径ts-node/register
,而后就能够在后边直接跟一个文件名(或者是一些通配符)。
目前咱们在项目中批量执行测试用例的命令以下:
{ "scripts": { "test": "mocha -r ts-node/register test/**/*.spec.ts" } }
npm test
能够直接调用,而不须要添加run
命令符,相似的还有start
、build
等等
一键执行之后就能够获得咱们想要的结果了,不再用担忧一些代码的改动会影响到其余模块的逻辑了 (前提是认真写测试用例)
作完上边两步的操做之后,咱们的项目就实现了100%的TypeScript
化,在任何地方享受静态编译语法所带来的好处。
附上更新后的代码含量截图:
最近针对TypeScript
作了不少事情,从Node.js
、React
以及此次的Webpack
与Mocha+Chai
。 TypeScript
由于其存在一个编译的过程,极大的下降了代码出bug的可能性,提升程序的稳定度。
全面切换到TypeScript
更是可以下降在两种语法之间互相切换时所带来的没必要要的消耗,祝你们搬砖愉快。
欢迎各位来讨论关于TypeScript
使用上的一些问题,针对稳重的感受不足之处也欢迎指出。