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 moduleimport * as koa from 'koa' 与 const koa = require('koa')Equivalent, but useimport * asThe created module object cannot actually be called and instantiated.
  • Similarly, TypeScript sees when importing a CommonJs/AMD/UMD moduleimport koa from 'koa' 与 const koa = require('koa').defaultEquivalent, 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