上期咱們說到了TypeScript裝飾器(decorators)和JavaScript裝飾器編譯出的代碼不一樣,雖然咱們的庫是用TypeScript寫的,但不少時候須要提供給JavaScript使用,因此這裏來講說怎麼將項目遷移至TypeScript
,並用babel
編譯。html
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.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' }, ] } }; 複製代碼
安裝須要的包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插件來處理TypeScript
。typescript
那麼,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來代替。
安裝
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
就能夠了,這裏就不做詳細介紹了。
可是這就結束了麼,no no no... 在編譯過程當中,咱們發現有些包的導入有問題 好比,將i18next
做爲外部資源引用時(webpack
的externals
能夠幫助咱們實現該方式),咱們發現代碼被編譯成
i18next_1['default'].t
複製代碼
可是i18next_1['default']
的值是undefined
,執行出錯 爲何?哪裏又雙叒叕...有問題了?😭
ECMAScript模塊在ES2015裏才被標準化,在這以前,JavaScript生態系統裏存在幾種不一樣的模塊格式,它們工做方式各有不一樣。 當新的標準經過後,社區遇到了一個難題,就是如何在已有的「老式」模塊模式之間保證最佳的互通性。
TypeScript與Babel採起了不一樣的方案,而且直到如今,還沒出現真正地固定標準。 在以前的版本,TypeScript 對 CommonJs/AMD/UMD 模塊的處理方式與 ES6 模塊不一樣,這會致使一些問題:
import * as koa from 'koa'
與 const koa = require('koa')
等價,但使用 import * as
建立的模塊對象實際上不可被調用以及被實例化。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'
複製代碼