遷移React項目至TypeScript(babel版)

上期咱們說到了TypeScript裝飾器(decorators)和JavaScript裝飾器編譯出的代碼不一樣,雖然咱們的庫是用TypeScript寫的,但不少時候須要提供給JavaScript使用,因此這裏來講說怎麼將項目遷移至TypeScript,並用babel編譯。html

安裝TypeScript

npm install typescript
複製代碼

書寫配置文件

TypeScript使用tsconfig.json文件管理工程配置,例如你想包含哪些文件和進行哪些檢查。 讓咱們先建立一個簡單的工程配置文件:node

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "strictNullChecks": false,
        "module": "commonjs",
        "target": "ESNext",
        "jsx": "react",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "moduleResolution": "node",
        "allowJs": false
    },
    "include": [
        "./src/**/*"
    ]
}
複製代碼

這裏咱們爲TypeScript設置了一些東西:react

讀取全部可識別的src目錄下的文件(經過include)。 接受JavaScript作爲輸入(經過allowJs)。 生成的全部文件放在dist目錄下(經過outDir)。 ... 你能夠在這裏瞭解更多關於tsconfig.json文件的說明。webpack

建立一個webpack配置文件

在工程根目錄下建立一個webpack.config.js文件。git

module.exports = {
    context: __dirname,
    entry: './src/index.tsx',
    output: {
        filename: 'bundle.js',
        path: `${__dirname}/dist`
    },

    // Enable sourcemaps for debugging webpack's output. devtool: "#source-map", resolve: { // Add '.ts' and '.tsx' as resolvable extensions. extensions: ['.js', '.ts', '.tsx'] }, module: { rules: [ // All files with a '.ts' or '.tsx' extension will be handled by 'babel-loader'. { test: /\.tsx?$/, loader: 'babel-loader' }, ] } }; 複製代碼

修改babel配置文件

安裝須要的包github

npm install @babel/preset-typescript @babel/plugin-transform-typescript
複製代碼

將上面安裝的包加入工程目錄下的babel.config.js文件。web

module.exports = {
    presets: ["@babel/preset-typescript", '@babel/preset-react', '@babel/preset-env', 'mobx'],
    plugins: [
        ['@babel/plugin-transform-typescript', { allowNamespaces: true }],
        // ... other
    ]
}
複製代碼

這裏咱們使用@babel/plugin-transform-typescript插件來處理TypeScripttypescript

那麼,TypeScript的類型檢測怎麼辦呢?不是至關於廢了嗎?這裏咱們使用 fork-ts-checker-webpack-plugin來啓用TypeScript類型檢測。express

#配置TypeScript類型檢查器npm

Install

npm install fork-ts-checker-webpack-plugin fork-ts-checker-notifier-webpack-plugin
複製代碼

webpack.config.js

const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
const ForkTsCheckerNotifierWebpackPlugin = require('fork-ts-checker-notifier-webpack-plugin');

module.exports = {
    // ...
    plugins: [
        new ForkTsCheckerWebpackPlugin({
            // 將async設爲false,能夠阻止Webpack的emit以等待類型檢查器/linter,並向Webpack的編譯添加錯誤。
            async: false
        }),
        // 將TypeScript類型檢查錯誤以彈框提示
        // 若是fork-ts-checker-webpack-plugin的async爲false時能夠不用
        // 不然建議使用,以方便發現錯誤
        new ForkTsCheckerNotifierWebpackPlugin({
            title: 'TypeScript',
            excludeWarnings: true,
            skipSuccessful: true,
        }),
    ]
};
複製代碼

準備工做完成。 終於能試試期待已久的TypeScript了,心情好happy 😜 可是,等等,What?爲何報錯了?

TS2304: Cannot find name 'If'.
TS2304: Cannot find name 'Choose'.
TS2304: Cannot find name 'When'.
複製代碼

原來是咱們在React項目中使用了jsx-control-statements致使的。 怎麼辦?在線等,挺急的... 😜 咱們發現,這裏咱們能夠用tsx-control-statements來代替。

配置 tsx-control-statements

安裝

npm install tsx-control-statements
複製代碼

tsconfig.json文件的files選項中添加

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "strictNullChecks": false,
        "module": "commonjs",
        "target": "ESNext",
        "jsx": "react",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "moduleResolution": "node",
        "allowJs": false
    },
    "files": [
        "./node_modules/tsx-control-statements/index.d.tsx"
    ]
}
複製代碼

接下來咱們按照TypeScript官網指南來把咱們的代碼改爲TypeScript就能夠了,這裏就不做詳細介紹了。

更便利的與ECMAScript模塊的互通性

可是這就結束了麼,no no no... 在編譯過程當中,咱們發現有些包的導入有問題 好比,將i18next做爲外部資源引用時(webpackexternals能夠幫助咱們實現該方式),咱們發現代碼被編譯成

i18next_1['default'].t
複製代碼

可是i18next_1['default']的值是undefined,執行出錯 爲何?哪裏又雙叒叕...有問題了?😭

ECMAScript模塊在ES2015裏才被標準化,在這以前,JavaScript生態系統裏存在幾種不一樣的模塊格式,它們工做方式各有不一樣。 當新的標準經過後,社區遇到了一個難題,就是如何在已有的「老式」模塊模式之間保證最佳的互通性。

TypeScript與Babel採起了不一樣的方案,而且直到如今,還沒出現真正地固定標準。 在以前的版本,TypeScript 對 CommonJs/AMD/UMD 模塊的處理方式與 ES6 模塊不一樣,這會致使一些問題:

  • 當導入一個 CommonJs/AMD/UMD 模塊時,TypeScript 視 import * as koa from 'koa'const koa = require('koa') 等價,但使用 import * as 建立的模塊對象實際上不可被調用以及被實例化。
  • 相似的,當導入一個 CommonJs/AMD/UMD 模塊時,TypeScript 視 import koa from 'koa'const koa = require('koa').default 等價,但在大部分 CommonJs/AMD/UMD 模塊裏,它們並無默認導出。

在 2.7 後的版本里,TypeScript提供了一個新的 esModuleInterop標記,旨在解決上述問題。 當使用這個新的esModuleInterop標記時,可調用的CommonJS模塊必須被作爲默認導入:

import express from "express";

let app = express();
複製代碼

咱們將其加入tsconfig.json文件中

{
    "compilerOptions": {
        "outDir": "./dist/",
        "sourceMap": true,
        "noImplicitAny": true,
        "strictNullChecks": false,
        "module": "commonjs",
        "target": "ESNext",
        "jsx": "react",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true,
        "allowSyntheticDefaultImports": true, // 容許使用 ES2015 默認的 import 風格
        "esModuleInterop": true, // 可調用的CommonJS模塊必須被作爲默認導入,在已有的「老式」模塊模式之間保證最佳的互通性
        "moduleResolution": "node",
        "allowJs": false
    },
    "files": [
        "./node_modules/tsx-control-statements/index.d.tsx"
    ]
}
複製代碼

到了這裏,咱們的程序終於能完美的運行起來了。 咱們不想再區分哪些須要使用import * as,哪些使用import,所以咱們將格式統一爲

import XX from 'XX'
複製代碼
相關文章
相關標籤/搜索