Last time we talked about how TypeScript decorators and JavaScript decorators compile different code. Although our libraries are written in TypeScript, they often need to be made available to JavaScript. So here’s how to migrate your project to TypeScript and compile it with Babel.
Install the TypeScript
npm install typescript
Copy the code
Writing configuration files
TypeScript uses tsconfig.json files to manage project configuration, such as which files you want to include and which checks. Let’s start with a simple project configuration file:
{
"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/**/*"]}Copy the code
Here we set a few things for TypeScript:
Read all recognizable files in the SRC directory (via include). Accept JavaScript as input (via allowJs). All generated files are placed in the dist directory (via outDir). . You can read more about the tsconfig.json file here.
Create a WebPack configuration file
Create a webpack.config.js file in the project root directory.
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'},]}};Copy the code
Modify the Babel configuration file
Install the required packages
npm install @babel/preset-typescript @babel/plugin-transform-typescript
Copy the code
Add the package installed above to the babel.config.js file in the project directory.
module.exports = {
presets: ["@babel/preset-typescript".'@babel/preset-react'.'@babel/preset-env'.'mobx'],
plugins: [
['@babel/plugin-transform-typescript', { allowNamespaces: true }],
// ... other
]
}
Copy the code
We use the @babel/plugin-transform-typescript plugin to handle typescript.
So what about TypeScript type detection? Isn’t it kind of obsolete? Here we use fork-ts-checker-webpack-plugin to enable TypeScript type detection.
Configure the TypeScript type checker
Install
npm install fork-ts-checker-webpack-plugin fork-ts-checker-notifier-webpack-plugin
Copy the code
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 ({/ / setting async asfalse, you can block Webpack emit to wait for the type checker /linter and add errors to the compilation of Webpack. async:false}), // make TypeScript type checking errors pop up // if fork-ts-checker-webpack-plugin async isfalseDon't have to / / it is recommended to use, otherwise the discovery of the error to facilitate new ForkTsCheckerNotifierWebpackPlugin ({title:'TypeScript',
excludeWarnings: true,
skipSuccessful: true,})]};Copy the code
The preparations are complete. Happy to finally try TypeScript you’ve been waiting for happy π But wait, What? Why is it wrong?
TS2304: Cannot find name 'If'.
TS2304: Cannot find name 'Choose'.
TS2304: Cannot find name 'When'.
Copy the code
It turns out that we used jSX-Control-statements in the React project. How to do? Online waiting, pretty urgent… π we found that we could use tsX-Control-statements instead.
Configuration TSX – control – statements
The installation
npm install tsx-control-statements
Copy the code
Add it in the files option of the tsconfig.json file
{
"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"]}Copy the code
Instead of going into detail here, we can change our code to TypeScript by following the TypeScript official website guide.
Easier interoperability with ECMAScript modules
But is it over? No, no, no… During compilation, we found some package import issues, such as when i18Next was referenced as an external resource (webpack externals helped us with this), and we found that the code was compiled to
i18next_1['default'].t
Copy the code
I18next_1 [‘default’] = undefined Where and double ε yi… Question? π
Before ECMAScript modules were standardized in ES2015, there were several different module formats in the JavaScript ecosystem that worked differently. When the new standards were adopted, the community was faced with the challenge of ensuring optimal interoperability between the existing “old” modular patterns.
TypeScript takes a different approach than Babel, and until now, there has been no real fixation of standards. In previous versions, TypeScript handled CommonJs/AMD/UMD modules differently than ES6 modules, which caused some problems:
- TypeScript looks at when importing a CommonJs/AMD/UMD module
import * as koa from 'koa'
δΈconst koa = require('koa')
Equivalent, but useimport * as
The created module object cannot actually be called and instantiated. - Similarly, TypeScript sees when importing a CommonJs/AMD/UMD module
import koa from 'koa'
δΈconst koa = require('koa').default
Equivalent, but in most CommonJs/AMD/UMD modules they do not export by default.
In version 2.7, TypeScript provides a new esModuleInterop flag that addresses this problem. When using the new esModuleInterop flag, callable CommonJS modules must be imported as default:
import express from "express";
let app = express();
Copy the code
Let’s add it to the tsconfig.json file
{
"compilerOptions": {
"outDir": "./dist/"."sourceMap": true."noImplicitAny": true."strictNullChecks": false."module": "commonjs"."target": "ESNext"."jsx": "react"."experimentalDecorators": true."emitDecoratorMetadata": true."allowSyntheticDefaultImports": true// The default import style of ES2015 is allowed"esModuleInterop": true// Callable CommonJS modules must be imported by default to ensure optimum interoperability between existing "old" module patterns"moduleResolution": "node"."allowJs": false
},
"files": [
"./node_modules/tsx-control-statements/index.d.tsx"]}Copy the code
At this point, our program is finally working perfectly. We don’t want to distinguish between import * as and import anymore, so we stick to the format
import XX from 'XX'
Copy the code