Migrating my React (create-react-app) application to Typescript

1 – Installing Typescript

Assuming that we created the project using create-react-app, now we have to install Typescript.

You can install it running this command:

npm install --save typescript @types/node @types/react @types/react-dom @types/jest

2 – Add the tsconfig.json.

Just run this command:

npx tsc --init

The presence of a tsconfig.json file in a directory indicates that the directory is the root of a TypeScript project. The tsconfig.json file specifies the root files and the compiler options required to compile the project.

JavaScript projects can use a jsconfig.json file instead, which acts almost the same but has some JavaScript-related compiler flags enabled by default.

https://www.typescriptlang.org/docs/handbook/tsconfig-json.html

3 – Rename any component,

For example Button.js -> Button.tsx

Then run npm start and you will see a message like that:

> react-scripts start

The following changes are being made to your tsconfig.json file:
  - compilerOptions.lib to be suggested value: dom,dom.iterable,esnext (this can be changed)
  - compilerOptions.allowJs to be suggested value: true (this can be changed)
  - compilerOptions.allowSyntheticDefaultImports to be suggested value: true (this can be changed)
  - compilerOptions.module must be esnext (for import() and import/export)
  - compilerOptions.moduleResolution must be node (to match webpack resolution)
  - compilerOptions.resolveJsonModule must be true (to match webpack loader)
  - compilerOptions.isolatedModules must be true (implementation limitation)
  - compilerOptions.noEmit must be true
  - compilerOptions.jsx to be suggested value: react (this can be changed)
  - include should be src

If you are using Windows and it gives you a error message like that…

FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory

Just run this command below. It is a workaround to fix heap out of memory when running node binaries (which is a common issue when using TypeScript 2.1+ and webpack) is increasing the max memory for node.

set NODE_OPTIONS=--max_old_space_size=8192

4 – Now we have to change our Button.tsx to Typescript

Before:

import React from 'react';
import BaseIcon from '../Icons/BaseIcon';
import './Button.scss';

export default function Button({
  id,
  label,
  icon,
  ariaLabel,
  size,
  colorHover,
  onClick,
  children,
  role,
}) {
  return (
    <button
      id={id || null}
      type="button"
      aria-label={ariaLabel || label || children}
      name={label || children}
      className={`button button-default color-${colorHover || 'default'} ${
        icon && 'has-icon'
      } size-${size || 'md'}`}
      onClick={onClick}
      role={role || null}
    >
      <span className={label || children ? 'has-label' : 'no-label'}>
        {label || children}
        {icon && <BaseIcon icon={icon} color="purple" />}
      </span>
    </button>
  );
}

After:

import React, { MouseEventHandler, ReactNode,  } from 'react';
import BaseIcon from '../Icons/BaseIcon'; 
import './Button.scss';

type Props = {
  id: string,
  label: string,
  icon: string,
  ariaLabel: string,
  size: string,
  colorHover: string,
  role: string, 
  onClick: MouseEventHandler,
  children: ReactNode,
}

export default function Button({
  id,
  label,
  icon,
  ariaLabel,
  size,
  colorHover,
  onClick,
  children,
  role,
}: Props) {
  return (
    <button
      id={id || undefined}
      type="button"
      aria-label={ariaLabel || label}
      name={label || undefined}
      className={`button button-default color-${colorHover || 'default'} ${
        icon && 'has-icon'
      } size-${size || 'md'}`}
      onClick={onClick}
      role={role || undefined}
    >
      <span className={label || children ? 'has-label' : 'no-label'}>
        {label || children}
        {icon && <BaseIcon icon={icon} color="purple" />}
      </span>
    </button>
  );
}

5 – Convert all files to Typescript

It can be lots of work but it will be worth. If the project you’re migrating is rather large, I suggest doing this over multiple iterations. Otherwise, you may tire yourself out.

During this step, you may need to add additional packages depending on what third-party libraries you’re using. For instance, I am using moment so I had to run yarn add -D @types/moment to add the types as a devDependency.