公司里最近让我负责一个项目,我试着引入了typescript,感受一下强类型的威力,原先已经有个react项目模版,改成了ts版的,这里把工作的笔记写在这里,可能不太全,将就着记录。
尝试步骤
- 添加编译设置
- 修改js代码至ts
- types文件改动
编译选项设置
- typescript官网有一个如何在React使用typescript的文档,里面介绍的是用create-react-app创建一个app脚手架,但是问题是创建出来的项目是用一个react-ts-scripts的工具编译的。我观察了很久没找到它是不是用webpack的,觉得似乎没什么可以配置的地方,最终还是用webpack中typescript的loader,这样可以用一套更加熟悉的工作流。
- loader可以使用
ts-loader
或者awesome-typescript-loader
,这两个loader都出现在了官方的文档中,我使用的是awesome-typescript-loader
,据文档称比ts-loader快一些。同时也在原来配置eslint-loader
的位置改成了配置tslint-loader
- 在项目根目录下添加了2个文件:
tsconfig.json
以及tslint.json
,这两个文件设置了一些基本的ts编译选项。类比一下,和.babelrc
或者eslint.json
差不多。文件内容来源于一个github上的一个react-ts-boilerplate - 我曾经想尝试只用
awesome-typescript-loader
而不用babel-loader
,但是编译出来的代码虽然target是es5,但有些浏览器不能跑(这点我一直在疑惑为什么),放在微信web开发者工具里面一直报错,所以在项目里ts编译后还是用了babel的loader再转一下,保证和原来一样能够运行。
修改js代码至ts代码
- 首先,就是把js代码扩展名变成ts,然后就开始根据ts的编译错误提示进行修改。
- 需要注意的一点是模块引用有着些许的不同,引入模块默认的export要按照as的语法写,例如原来引入React是
import React from 'react';
typescript中必须这样
import * as React from 'react';
- 为函数以及变量添加类型,具体定义类型的语法一般是
: type
,如果不确定可以通过加any
类型使代码被编译器所接受。 - 全局变量的操作,有一些代码依赖于别的script tag,如微信的JS SDK,会在window下面挂一个wx对象。这时候如果直接调用
wx.someAPI()
,那么ts检查会报错,没有wx对象。但是如果调用window.wx,编译器还是会报错,因为window对象在types里已经有了一个window类型,下面没有wx这个field,除了改写types文件,还可以用类型断言
的语法:
(<any> window).wx.someAPI();
// 或者
(window as any).wx.someAPI();
- React相关写法。typescript中的类型定义中,interface是一个比较重要的语法,可以规定object中有哪些类型的field。这个就非常适合定义React组件的props和state的类型,typescript会对调用组件用的props进行类型检查,React定义为了一个泛型,一个标准写法如下:
interface someStateType {
...
}
interface somePropsType {
...
}
class someComponent extends React.Component<somePropsType, someStateType> {
...
}
types配置
- 安装@types包,一般常用的js第三方库目前都有了声明文件,在membership-h5项目中,package.json配置了以下包:
"@types/node": "^9.3.0",
"@types/react": "^16.0.34",
"@types/react-dom": "^16.0.3",
"@types/react-router-dom": "^4.2.3",
"@types/react-tap-event-plugin": "0.0.30",
有了这些包后,例如在用react代码时,ts检测到是js包,则会自动到node_modules/@types/目录下去找react的声明文件,那静态检查时用的规则就是按照声明文件为主。
- 但是如果是其他第三方库,有可能没有types文件。
- 有的时候是这样解决的,例如
某公司内部库,
会在所有js文件加一个同名的.d.ts文件,这样引入进来时直接把.d.ts引入进来 - membership项目中采取的是这样的方式,自己觉得最好不要修改node_modules已经存在的东西,公共库还不是太了解,没有很大胆量去做声明文件。
tsconfig.json
中,有一个编译选项,是typeRoots
。于是在配置里面加上了这句,用dts目录 放.d.ts文件
- 有的时候是这样解决的,例如
"typeRoots" : ["./node_modules/@types", "./src/dts"]
- 可以建立专门的types文件为业务数据建模,dts目录下写了
commodity.d.ts
,定义了一些业务数据的类型和存在的fields,如积分商品,兑换记录。这样定义好还有文档的作用。
todos
- 取消项目中现在暂时填写any的部分,完善类型。
- 调查有没有办法完全取消babel-loader。
- 利用typescript的静态类型检查做更加完善的数据校验函数,防止出现一些取undefined下数据的异常。
- 找到一些tslint warning的比较好的解决办法。