遷移React項目至TypeScript

關於 TypeScript

TypeScript 是 JavaScript 的一個超集,主要提供了類型系統對 ES6 的支持,它由 Microsoft 開發,代碼開源於 GitHub 上。html

它的第一個版本發佈於 2012 年 10 月,經歷了屢次更新後,如今已成爲前端社區中不可忽視的力量。TypeScript是一種靜態類型語言,增長了代碼的可讀性和可維護性,應用愈來愈普遍。接下來,咱們來看下如何遷移本身的項目至TypeScript。前端

安裝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": true
    },
    "include": [
        "./src/**/*"
    ]
}
複製代碼

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

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

建立一個webpack配置文件

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

module.exports = {
    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 'awesome-typescript-loader'. { test: /\.tsx?$/, loader: 'awesome-typescript-loader', options: { useCache: true, // Use internal file cache useBabel: true, // Invoke Babel to transpile files babelCore: '@babel/core' } }, ] } }; 複製代碼

這裏咱們設置useBabeltrue, 調用babel生成文件。github

修改babel配置文件

安裝須要的包web

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

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

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

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

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": true
    },
    "files": [
        "./node_modules/tsx-control-statements/index.d.tsx"
    ]
}
複製代碼

在webpack的配置文件webpack.config.js中添加

const statements = require('tsx-control-statements').default;

module.exports = {
    ...
    module: {
        rules: [
            // All files with a '.ts' or '.tsx' extension will be handled by 'awesome-typescript-loader'.
            {
                test: /\.tsx?$/,
                loader: 'awesome-typescript-loader',
                options: {
                    useCache: true, // Use internal file cache
                    useBabel: true, // Invoke Babel to transpile files
                    babelCore: '@babel/core',
                    // Add tsx-control-statements here
                    getCustomTransformers: () => ({ before: [statements()] })
                }
            },
        ]
    },
    ...
};
複製代碼

接下來咱們按照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": true
    },
    "files": [
        "./node_modules/tsx-control-statements/index.d.tsx"
    ]
}
複製代碼

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

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