ReactJS - Static Type Checking



Since JavaScript is a dynamically typed language, it is hard to find type mismatch error before running the code. React support type checking for props through prop-types package, which can be used to identity type mismatch for properties during development phase.

The other aspect of the program still needs a tool to properly identify the type issues during development phase. JavaScript has lot of static type checker tools to handle the type problem. We will check two of the popular option, which will integrate smoothly into the workflow of the React application and give hints of the possible type error during development phase of the application. They are as follows −

  • Flow

  • TypeScript language

Flow

Flow is static type checker for JavaScript. Flow extends the JavaScript language to specify type and allows static type annotation to be set inside the JavaScript code. Flow will check the static type annotation set by the developer in the code and make sure the proper type is used. Otherwise, it will throw error. A simple example is as follows −

// @flow
function sum(a: number, b: number) : number {
   return a + b;
}
sum(10, 20)     // Pass
sum("10", "20") // Fail

Here, // @flow annotation enables the flow static checker to analyze the function defined below. As you see, the function sum uses the Flow language extension to specify the type of the argument. Let us see how to enable Flow in a react project and the development workflow of the Flow enabled react project.

Step1 − Create a new react project using create-react-app CLI app.

create-react-app myapp

Step 2 − Add Flow to the project using below command

cd myapp
npm install --save-bin flow-bin

Step 3 − Now, add Flow command in the scripts of the package.json file

{
   // ...
   "scripts": {
      "flow": "flow",
      // ...
   },
   // ...
}

This will allow us to run flow command through npm

Step 4 − Initialize the flow configuration using flow command as shown below −

npm run flow init

This will create a basic flow configuration file .flowconfig at the root of the project with below content.

[ignore]
[include]
[libs]
[lints]
[options]
[strict]

Advanced flow options can be added here. By default, flow will check all files in our application. To ignore node_modules, add .*/node_modules/.* under [ignore] option. This will instruct the flow application to ignore all files inside the node_modules folder.

[ignore]
.*/node_modules/.*
[include]
[libs]
[lints]
[options]
react.runtime=automatic
[strict]

Step 5 − Now, we have configured the flow into our application. We can use flow annotation into our code and test it against flow using below command

npm run flow

Flow will check our code and show the similar result in the console as shown below −

> myapp@0.1.0 flow /path/to/myapp
> flow
Launching Flow server for /path/to/myapp
Spawned flow server (pid=1629)
Logs will go to /private/tmp/flow/zSUserszSbalazSProjectszSArticleszSreact-revision-v2zSworkspacezSmyapp.log
Monitor logs will go to /private/tmp/flow/zSUserszSbalazSProjectszSArticleszSreact-revision-v2zSworkspacezSmyapp.monitor_log
No errors!

Step 6 − Now, it is fine to use flow annotation in our code. Let us add a simple flow annotation into our code and run the flow command to check the correctness of the code. Create a simple HelloWorld component (src/components/HelloWorld.js) as shown below −

import React from 'react'
class HelloWorld extends React.Component {
   render() {
      return <h1>Hello, {this.props.name}</h1>
   }
}
export default HelloWorld

Step 7 − Include the component in our root component (App.js) as shown below −

// @flow
import React from "react";
import HelloWorld from "./components/HelloWorld";
function App() : any {
   var name: string = 10
   return (
      <div>
         <HelloWorld name={name} />
      </div>
   )
}
export default App;

Step 8 − Now, check the code with flow as shown below

npm run flow

Flow command will check the code and show the error that the name is set with string value as shown below.

> myapp@0.1.0 flow /path/to/myapp
> flow
Error ............................................src/App.js:6:22
Cannot assign 10 to name because number [1] is incompatible with string [2]. [incompatible-type]
      3│ import HelloWorld from "./components/HelloWorld";
      4│
      5│ function App() : any {
[2][1]6│   var name: string = 10
      7│
      8│   return (
      9│     <div>
Found 1 error
.....
.....

Step 9 − Let us fix the error by providing a string value for name variable and rerun the flow command.

// @flow
import React from "react";
import HelloWorld from "./components/HelloWorld";
function App() : any {
   var name: string = "John"
   return (
      <div>
         <HelloWorld name={name} />
      </div>
   )
}
export default App;

Now, flow command will succeed and show that there is no issue in the code.

> myapp@0.1.0 flow /path/to/myapp
> flow
No errors!

Step 10 − Finally, we can run the app by running below command,

npm start

The optimized production build can be created using below command,

npm run build

TypeScript

TypeScript is a language with first class support for static typing. Static typing enables TypeScript to catch the type error during compile time instead of runtime. TypeScript support all language features of JavaScript.

So, it is really easy for a JavaScript developer to work in TypeScript. React has build-in support for TypeScript. To create a React project, just include TypeScript template during the react app creation through create-react-app.

create-react-app myapp --template typescript

Once the application is created, add a new HelloWorld component (HelloWorld.tsx) under src/components folder as shown below −

// src/components/HelloWorld.tsx
import React from 'react'
class HelloWorld extends React.Component {
   render() {
      return <h1>Hello, {this.props.name}</h1>
   }
}
export default HelloWorld

Now, include the HelloWorld compon −

import React from "react";
import HelloWorld from "./components/HelloWorld";
function App() : any {
   var name: string = 10
   return (
      <div>
         <HelloWorld name={name} />
      </div>
   )
}
export default App;

Here, we have intentionally set the value of name to 10. Let us run the application and check whether the compiler catch the error.

npm start

Running the command throws error as shown below −

...
...
ERROR in src/App.tsx:5:7
TS2322: Type 'number' is not assignable to type 'string'.
     3 |
     4 | function App() : any {
   > 5 |   var name: string = 10
       |       ^^^^
     6 |
     7 |   return (
     8 |     <div>
...
...

Let us change the value of name and set a string value ("John"). The above error get fixed but still the compiler throws error in HelloWorld component as shown below −

Issues checking in progress...
ERROR in src/App.tsx:9:19
TS2769: No overload matches this call.
   Overload 1 of 2, '(props: {} | Readonly<{}>): HelloWorld', gave the following error.
      Type '{ name: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & Readonly<{}>'.
         Property 'name' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & Readonly<{}>'.
   Overload 2 of 2, '(props: {}, context: any): HelloWorld', gave the following error.
      Type '{ name: string; }' is not assignable to type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & Readonly<{}>'.
         Property 'name' does not exist on type 'IntrinsicAttributes & IntrinsicClassAttributes<HelloWorld> & Readonly<{}>'.
     7 |   return (
     8 |     <div>
> 9 |       <HelloWorld name={name} />
       |                   ^^^^
    10 |     </div>
    11 |   )
    12 | }
ERROR in src/components/HelloWorld.tsx:9:39
TS2339: Property 'name' does not exist on type 'Readonly<{}>'.
     7 | class HelloWorld extends React.Component {
     8 |     render() {
> 9 |        return <h1>Hello, {this.props.name}</h1>
       |                                       ^^^^
    10 |     }
    11 | }
    12 |

To fix error, HelloWorld component should be provided with type information for its properties. Create a new interface for properties and then include it in HelloWorld component as shown below −

import React from 'react'
interface HelloWorldProps {
   name: string
}
class HelloWorld extends React.Component<HelloWorldProps> {
   render() {
      return <h1>Hello, {this.props.name}</h1>
   }
}
export default HelloWorld

Finally, our code gets compiled with no errors as shown below and can be viewed through at `http://localhost:3000/

No issues found.
Static Type Checking
Advertisements