react项目启用ts

公司里最近让我负责一个项目,我试着引入了typescript,感受一下强类型的威力,原先已经有个react项目模版,改成了ts版的,这里把工作的笔记写在这里,可能不太全,将就着记录。

尝试步骤

  1. 添加编译设置
  2. 修改js代码至ts
  3. types文件改动

编译选项设置

  1. typescript官网有一个如何在React使用typescript的文档,里面介绍的是用create-react-app创建一个app脚手架,但是问题是创建出来的项目是用一个react-ts-scripts的工具编译的。我观察了很久没找到它是不是用webpack的,觉得似乎没什么可以配置的地方,最终还是用webpack中typescript的loader,这样可以用一套更加熟悉的工作流。
  2. loader可以使用ts-loader或者awesome-typescript-loader,这两个loader都出现在了官方的文档中,我使用的是awesome-typescript-loader,据文档称比ts-loader快一些。同时也在原来配置eslint-loader的位置改成了配置tslint-loader
  3. 在项目根目录下添加了2个文件: tsconfig.json以及tslint.json,这两个文件设置了一些基本的ts编译选项。类比一下,和.babelrc或者eslint.json差不多。文件内容来源于一个github上的一个react-ts-boilerplate
  4. 我曾经想尝试只用awesome-typescript-loader而不用babel-loader,但是编译出来的代码虽然target是es5,但有些浏览器不能跑(这点我一直在疑惑为什么),放在微信web开发者工具里面一直报错,所以在项目里ts编译后还是用了babel的loader再转一下,保证和原来一样能够运行。

修改js代码至ts代码

  1. 首先,就是把js代码扩展名变成ts,然后就开始根据ts的编译错误提示进行修改。
  2. 需要注意的一点是模块引用有着些许的不同,引入模块默认的export要按照as的语法写,例如原来引入React是
    import React from 'react';

typescript中必须这样

    import * as React from 'react';
  1. 为函数以及变量添加类型,具体定义类型的语法一般是: type,如果不确定可以通过加any类型使代码被编译器所接受。
  2. 全局变量的操作,有一些代码依赖于别的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();
  1. React相关写法。typescript中的类型定义中,interface是一个比较重要的语法,可以规定object中有哪些类型的field。这个就非常适合定义React组件的props和state的类型,typescript会对调用组件用的props进行类型检查,React定义为了一个泛型,一个标准写法如下:
    interface someStateType {
        ...
    }
    
    interface somePropsType {
        ...
    }
    
    class someComponent extends React.Component<somePropsType, someStateType> {
        ...
    }

types配置

  1. 安装@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的声明文件,那静态检查时用的规则就是按照声明文件为主。

  1. 但是如果是其他第三方库,有可能没有types文件。
    • 有的时候是这样解决的,例如某公司内部库,会在所有js文件加一个同名的.d.ts文件,这样引入进来时直接把.d.ts引入进来
    • membership项目中采取的是这样的方式,自己觉得最好不要修改node_modules已经存在的东西,公共库还不是太了解,没有很大胆量去做声明文件。tsconfig.json中,有一个编译选项,是typeRoots。于是在配置里面加上了这句,用dts目录 放.d.ts文件
    "typeRoots" : ["./node_modules/@types", "./src/dts"]
  1. 可以建立专门的types文件为业务数据建模,dts目录下写了commodity.d.ts,定义了一些业务数据的类型和存在的fields,如积分商品,兑换记录。这样定义好还有文档的作用。

todos

  1. 取消项目中现在暂时填写any的部分,完善类型。
  2. 调查有没有办法完全取消babel-loader。
  3. 利用typescript的静态类型检查做更加完善的数据校验函数,防止出现一些取undefined下数据的异常。
  4. 找到一些tslint warning的比较好的解决办法。