# 从 JavaScript 到 TypeScript,React 项目迁移几点 tips

相对于 JavaScript,TypeScript 增加了类型系统。通过静态检查,开发者可以更早地发现语法错误,同时类型标注也带来了清晰的文档。这篇文章记录了几个关于 React 项目从 JavaScript 迁移到 TypeScript 笔者处理过的一些问题,这些问题虽然并不复杂,但是如果在迁移的过程中遇到了,也需要花上不少的时间去调查和寻找解决方案。

首先推荐阅读微软的一篇 React 项目迁移文档

简单总结一下,整个迁移的过程可以分为下面两类操作:

  1. 配置 TypeScript 编译器。
  2. 将 JavaScript 文件转化为 TypeScript 文件。

配置编译器

1. 安装依赖

依赖可以分为两类,一类是 TypeScript 编译工具,另一类是第三方依赖的类型声明文件(declarations)。

安装编译工具,TypeScript 的主流 loader 有 ts-loader 和 awesome-typescript-loader.

npm install --save-dev typescript ts-loader source-map-loader

安装第三方依赖类型文件,下面的例子只包括了 react 和 react-dom,其他依赖声明可以根据自己项目进行安装。

npm install --save -D @types/react @types/react-dom

如果最终发布的内容是会被其他项目引用的工具,应该把 @types 包括在发布的文件当中,即 npm i @types/xxx,如果发布的内容不会被其他项目引用,那么安装为 devDependencies 即可,可以参考 stackoverflow 上的这篇讨论

如果项目开发时使用了热更新(HMR),类型检查会报错 module.hot 类型问题,需要安装 @types/webpack-env 来解决这个类型问题。

2. webpack 配置更新

  • 添加 resolve 的文件后缀:
resolve: {
  // changed from extensions: [".js", ".jsx"]
  extensions: [".ts", ".tsx", ".js", ".jsx"]
},
  • 使用 ts-loader 处理 js, jsx, ts, tsx 文件:
module: {
  rules: [
    // changed from { test: /\.jsx?$/, use: { loader: 'babel-loader' } },
    {
      test: /\.(t|j)sx?$/,
      use: {
        loader: ts-loader',
         options: { // options for spped up compilation
           transpileOnly: true
         }
       }
    },
    // addition - add source-map support
    { enforce: "pre", test: /\.js$/, loader: "source-map-loader" }
  ]
},

项目冷启动开发时(npm start),如果顺序进行类型检查和编译,会花费不少的时间。我们可以另开一个线程来进行类型检查,从而节约时间。fork-ts-checker-webpack-plugin 就是这样一个 webpack plugin,非常推荐使用。

3. 添加 ts 编译器配置

// tsconfig.js
{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "module": "es6",
        "target": "es5",
        "jsx": "react",
        "allowJs": true,
        "moduleResolution": "node",
        "allowSyntheticDefaultImports": true
    },
    "includes": [
      "./src/",
    ]
}

在之前的打包配置里面,babel 实现的功能主要有两项,包括:编译 jsx 语法(preset-react)和 将晚进的语法编译为 es5 语法(preset-env),从而实现更好的兼容性。而 TypeScript 编译器同样可以实现这些操作,那么如果 babel 配置没有其他特殊操作(比如编译其他特殊语法),那么完全可以使用 TypeScript loader 替代 babel。

tsconfig 文件里面可以配置编译器的各种选项。如果类型检查的时候,因为引入的组件没有 default export (通过 module.exports 输出),可能会报错,那么配置allowSyntheticDefaultImport 可以让 ts 忽略引用报错。

文件转换

源代码文件

从 .js 迁移到 .ts,主要是添加各种类型声明。

一些小 tips:

  • 事件类型(event):可以使用 React 事件注释:
export type InputEvent = React.ChangeEvent<HTMLInputElement>;
export type ButtonEvent = React.MouseEvent<HTMLButtonElement>;
  • 组件类型(element):组件可以用 JSX.Element 标注。
  • 组件方法声明问题,在 React 组件里面,有的时候会直接在 constructor 里面对创建的对象绑定方法:
constructor(props) {
  ...
  
  this.foo = () => {
    ...
  }
}

而下面这种方式是将方法声明给了该组件 class 的 prototype,然后在 constructor 里面绑定新建对象。

constructor(props) {
  ...
  
  this.foo = this.foo.bind(this);
}

foo() {
  ...
}

这两种写法对于程序运行没有实际的区别,但是第一种方式声明的方法并不在 prototype 上,所以类型检查无法将生成对象的方法和 class 方法对应起来,会报错。解决方法是按照第二种写法,将方法绑定到 class prototype 上即可。

样式文件

对于预处理样式(比如 stylus,sass,less等),需要相应的 declarations 文件,通过配置 css-modules-typescript-loader 可以帮助我们自动生成 d.ts 文件。

  1. 安装 css-modules-typescript-loader,在 webpack 配置其在 css-loader 之后处理样式文件。
  2. 创建一个 declaration 文件,声明预处理样式文件命名格式,因为我们的项目文件名字采用 .cm.styl 后缀表示,所以声明如下:
// declarations.d.ts
declare module '*.cm.styl';
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,639评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,277评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,221评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,474评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,570评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,816评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,957评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,718评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,176评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,511评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,646评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,322评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,934评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,755评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,987评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,358评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,514评论 2 348

推荐阅读更多精彩内容