Practice of installing and using TypeScript in old React projects

Keywords: Javascript TypeScript React Webpack npm

Preface

By default, this article gives you an overview of what TypeScript is, mainly explaining how to install and use it in older React projects.

The main purpose of writing this is that there are many online explanations of TypeScript, but they are grammatical concepts or simple examples. There are few articles that actually transform an old React project using TypeScript.

So here's a record of the practice of transforming a React project.

Blog Content Section Reference TypeScript Chinese Web This website has a Chinese version of the official documents.

Install TypeScript and related libraries

This step can be skipped for scaffolds that integrate TypeScript, but here's how to integrate TypeScript into a React scaffold.

Execute first

npm install --save @types/react @types/react-dom

This step is mainly to get the declaration files for react and react-dom, because not all libraries have TypeScript declaration files, so run

npm install --save @types/library name

To get the declaration file for TypeScript.

Type checking for this library is only possible if the declaration file is obtained.

If you're using some other library that doesn't declare files, you might want to do the same.

Then run the command:

npm install --save-dev typescript awesome-typescript-loader source-map-loader

In this step, we installed typescript, awesome-typescript-loader, and source-map-loader.

awesome-typescript-loader allows Webpack to compile TypeScript code using the standard configuration file tsconfig.json for TypeScript.

source-map-loader uses the source map file output by TypeScript to tell the webpack when to generate its own sourcemap s, source mapping, and debugging convenience.

Add TypeScript Profile

Create a tsconfig.json file in the project root directory, with the following content examples:

{
  "compilerOptions": {
    "allowSyntheticDefaultImports": true, // Allows default imports from modules that do not have a default export set.This does not affect the output of the code, just for type checking.
    "outDir": "./dist/", // Redirect Output Directory
    "sourceMap": true, // Generate corresponding.map file
    "noImplicitAny": true, // Error occurs when there is an implied any type on the expression and declaration.
    "module": "esnext", // Module Introduction Mode
    "target": "esnext", // Specify the target version of ECMAScript
    "moduleResolution": "node", // Decide how to handle modules
    "lib": [
      "esnext",
      "dom"
    ], // List of library files to be introduced during compilation.
    "skipLibCheck": true, //Ignore type checking for declaration files (*.d.ts) in all libraries.
    "jsx": "react" // Support for JSX in.tsx files
  },
  "include": [
    "./src/**/*", // This means processing all.ts and.tsx files in the src directory of the root directory, not all files
  ]
}

SkpLibCheck is important, not every library can be detected by typescript.

Setting moduleResolution to node is also important.If this is not the case, typescript will not be found in the node_modules folder when looking for the declaration file.

More profile information can be referenced: Here.

Configure webpack

Here are some configurations that TypeScript needs to use in the webpack.

rule configuration for parsing tsx files

Examples are as follows:

module: {
    rules: [
      {
        test: /\.jsx?$/,
        exclude: /(node_modules)/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['react', 'env', 'stage-0', 'stage-3'],
            plugins: [
              'transform-decorators-legacy',
              ['import', { libraryName: 'antd', style: 'css' }], // `style: true` Loads a less file
            ],
          },
        },
      },
      { test: /\.tsx?$/, loader: "awesome-typescript-loader" }
      //...
    ]
    //...
}

It's just adding one more line:

{ test: /\.tsx?$/, loader: "awesome-typescript-loader" }

Note that this line needs to be added under the rule that resolves jsx, since the execution order of the rule is from bottom to top, resolving tsx and ts before resolving js and jsx.

Of course

enforce: 'pre'

This can be ignored by those who have adjusted the rule order.

Solve problems with css-moudule

If the following code is used in the code:

import styles from './index.css'

Then the following error is likely to be reported:

Cannot find module './index.css'

The solution is to create a new file called declaration.d.ts in the root directory, which contains:

declare module '*.css' {
  const content: any;
  export default content;
}

This line of code is the declaration for all css files.

We also need to change our previous tsconfig.json file to include this file path:

"include": [
  "./src/**/*", 
  "./declaration.d.ts"
]

There are ways to solve this problem by installing some libraries, but it seems strange to have a declaration file generated for each css, and I've considered using the above method for myself.

Configuration for omitting suffix names

If you are used to using

import Chart from './Chart/index.jsx'

When the suffix is omitted, that is:

import Chart from './Chart/index'

Then ts and tsx also need to be added to the resolve of the webpack:

resolve: {
  extensions: [".ts", ".tsx", ".js", ".jsx"]
},

Introducing Ant Design

How does this actually work in TypeScript on Ant Design s'official website? Use in TypeScript.

So why list them anyway?

Because it's important to note that for older projects that already have Ant Design s installed (typically loaded on demand), the above documentation is of little use when installing TypeScript.

All that you can find on the web seems to be schemas in the documentation, but all we need to do is install ts-import-plugin.

npm i ts-import-plugin --save-dev

Then, with the previous awesome-typescript-loader, configure the following in the webpack

const tsImportPluginFactory = require('ts-import-plugin')

module.exports = {
  // ...
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        loader: "awesome-typescript-loader",
        options: {
          getCustomTransformers: () => ({
            before: [tsImportPluginFactory([
              {
                libraryName: 'antd',
                libraryDirectory: 'lib',
                style: 'css'
              }
            ])]
          }),
        },
        exclude: /node_modules/
      }
    ]
  },
  // ...
}

Configuration complete, preparation before modification

Note that until this point, in fact, your project still doesn't use TypeScript during compilation.

Because we will only use TypeScript to process files with the.ts and.tsx suffixes here, unless allowJs is set to true in the configuration.

By default, you already know the TypeScript syntax before you use it, so you can refer to it: 5 Minutes Beginner TypeScript.

That is, after these steps, your original code should continue to work without changing the suffix.

If you want to use TypeScript, create new tsx and ts files or modify the original file suffix name.

Following are some typical examples of modifications.

Examples of modifying functional components (including children)

import React from 'react'
import styles from './index.css'

interface ComputeItemProps {
  label: string;
  children: React.ReactNode;
}

function ComputeItem({ label, children }: ComputeItemProps) {
  return <div className={styles['item']}>
    <div className={styles['label']}>{label}:</div>
    <div className={styles['content']}>{children}</div>
  </div>
}
export default ComputeItem

//The syntax in this example can be found on the official website of TypeScript. The only thing to note is that the type of children is React.ReactNode.

class component modification example (with function declaration, definition of event parameter)

import React from 'react'
import styles from './index.css'

interface DataSourceItem {
  dayOfGrowth: string;
  netValueDate: string;
}

interface ComputeProps {
  fundCode: string;
  dataSource: DataSourceItem[];
  onChange(value: Object): void;
}

export default class Compute extends React.Component<ComputeProps, Object> {
  // Change Fund Code
  handleChangeFundCode = (e: React.ChangeEvent<HTMLInputElement>) => {
    const fundCode = e.target.value
    this.props.onChange({
      fundCode
    })

  }  
  render() {
      //...
    );
  }
}

This example shows how to declare a class component:

React.Component<ComputeProps, Object>

Grammar may seem strange, but it's a generic in TypeScript and should be well understood if you've had C# or Java experience before.

The first defines the type of Props and the second defines the type of State.

The event parameter type for react should be defined as follows:

React.ChangeEvent<HTMLInputElement>

Generics are also used here, which represent the type of Exchange event for Input.

A function type is defined on a component's Prop and is not listed separately here.

These are some of the more typical examples of a combination of TypeScript and React.

Processing variables on Windows

Using global variables written on windows will prompt that this property does not exist on windows.

To handle this, you can define variables in the declaration.d.ts file:

// Define a window variable
interface Window{
  r:string[]
}

Where r is the variable name.

summary

I wanted to write a few more examples, but the Dota2 version has been updated, so I don't want to write any more. I'll update the commonly used examples later if I have time.

This article only focuses on installing and integrating TypeScript in old React projects, as far as possible without the specific syntax and introduction of TypeScript, since it's not something that a blog can do about it.

Please correct any omissions in this article in the hope of helping you hesitate in front of TypeScript.

Posted by darklight on Wed, 27 Nov 2019 17:42:01 -0800