Migrating from create-react-app-typescript to Create React App

Create React App 2.1.0 just arrived with TypeScript support! While Will Monk's fork create-react-app-typescript has served us well, being able to use the official version has a number of advantages. Most importantly: it is supported by the full weight of the Create React App team, and will therefore stay closely aligned with React proper and will always have up-to-date documentation. Furthermore, you are able to use everything that is supported by Create React App, like Sass.

The implementation also deviates a bit from create-react-app-typescript's. Most importantly, TypeScript is only used for type checking, whereas transpilation is done by Babel. The disadvantage of this is that you are behest to the caveats of Babel's TypeScript support, most notably the lack of namespaces and having to use the x as Foo type casting syntax. In practice, however, it is unlikely that these caveats will affect a React app, and the upside is that you are now able to tap into Babel's extensive ecosystem.

So great: we can now use TypeScript for new apps created with Create React App. However, many of us already have apps written using create-react-app-typescript. How much work is it to port those to Create React App proper?

As it turns out: not that much. Let's get to it.

Step 1: remove react-scripts-ts, add react-scripts

Create React App is a command line application that generates a basic React application for you, but instead of adding all commonly used dependencies directly, it adds a single dependency that bundles them and is maintained by the CRA team itself: react-scripts. Likewise, create-react-app-typescript has its own fork of this: react-script-ts. Thus, the main thing to do when migrating, is switching over this dependency:

$ npm uninstall react-scripts-ts
$ npm install react-scripts

Then, we have to make sure that the new scripts are the ones that are actually called when we run npm start, npm test, etc. Thus, in your package.json, replace:

  "scripts": {
    "watch": "react-scripts-ts start",
    "build": "react-scripts-ts build",
    "test": "react-scripts-ts test --env=jsdom",
    "eject": "react-scripts-ts eject",

with

  "scripts": {
    "watch": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject",

Step 2: Activate TypeScript support

Although CRA now supports TypeScript, you do have to explicitly enable it. This can be done by simply installing a few packages:

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

Step 3: Clean up the remnants of create-react-app-typescript

create-react-app-typescript did a few things differently from how Create React App proper does it, and therefore added a few files that now are no longer needed or are now named differently. Don't worry, though; we will recreate the relevant and properly named files in a moment.

The outdated files are tsconfig.json, tsconfig.prod.json, tsconfig.test.json and images.d.ts. To remove them with a single command:

$ rm tsconfig.json tsconfig.prod.json tsconfig.test.json images.d.ts

(Note that, apart from a few options, you will still be able to customise tsconfig.json to your likings.)

Step 4: Run it!

You should now be set! If you now run your app for the first time, Create React App will create relevant files, such as a new tsconfig.json, for you:

$ npm start

If everything went well, you should now have a running app. Not so bad, was it?

Depending on your setup, though, there might be a few additional problems you might run into.

Troubleshooting

I will try to keep the following list up-to-date with problems that people run into, and how to solve them. Ran into any yourself that was not documented here? Let me know on Twitter or <a href="mailto:[email protected]" title="Send me an email">by email</a>.

Absolute imports

create-react-app-typescript allowed specifying your imports relative to your root directory. In other words, no matter which file you were editing, you could do

// In src/components/Bar/Bar.tsx
import { Foo } from 'src/components/Foo/Foo.tsx';

This is useful because it allows you to move your files around as you please, but also makes it less transparent where your imports are coming from.

Create React App does not support this. To fix this, make your imports relative:

// In src/components/Bar/Bar.tsx
import { Foo } from '../Foo/Foo.tsx';

Importing CSS files from node_modules

In create-react-app-typescript, you could directly import CSS files that were located in node_modules from within a React component:

// In Foo.tsx
import 'node_modules/bulma/css/bulma.min.css';

With Create React App, you can still import from node_modules but, like above, can not rely on absolute imports. However, you can import directly from subfolders in node_modules:

// In Foo.tsx
import 'bulma/css/bulma.min.css';

Value not found, property does not exist on type, etc.

If you were using modern JavaScript API's in your code, you have to tell TypeScript to include the relevant type definitions. To do so, add the lib property to the compilerOptions in your tsconfig.json, and add the type definitions you use, e.g.:

    "lib": ["esnext", "dom"]

Also make sure that, if you want to support older browsers like Internet Explorer 11, you include the relevant polyfills.

Parsing error: Unexpected token

This can be the result of an ESLint configuration that does not incorporate Create React App's default configuration. If you have an .eslintrc file (possibly with an extension), add:

  "extends": [
    "react-app",
    "react-app/jest"
  ]

(or add those two values to an existing extends property if present.)

If you don't have an .eslintrc file, add the following to your package.json:

  "eslintConfig": {
    "extends": [
      "react-app",
      "react-app/jest"
    ]
  },

(or add this to an existing eslintConfig property if present.)

For more info, see https://github.com/facebook/create-react-app/issues/9791.

Thanks to Justus R for getting in touch to share this!

Other type checking errors

Create React App enables strict mode for TypeScript. This can help you catch many errors, and I would suggest you to try to fix the errors you encounter. That said, create-react-app-typescript only enabled a subset of the strict type checking options, and moving to a stricter mode now might be too much of a hassle. To loosen these restrictions, you can try to manually disable the checks you are violating in the compilerOptions in your tsconfig.json, e.g.:

    "alwaysStrict": false,
    "strictFunctionTypes": false,
    "strictPropertyInitialization": false,

Allocation failed - JavaScript heap out of memory

If you use Yarn, adding or removing dependencies might start to fail. This is likely due to Yarn not being able to process the untold masses of dependencies. One factor that can strongly bloat your number of dependencies is if you are using Storybook 3, because it includes other versions of Webpack, Babel, etc. than Create React App 2 is using. Luckily, Storybook also just released a new version that should be compatible with the package versions used by Create React App, so following the Storybook v4 upgrade instructions should solve this issue. Be sure to also read the paragraph below to make Storybook work.

Storybook

Storybook uses its own Webpack configuration to load your stories. It uses babel-loader and, if you followed the official docs on using TypeScript with Storybook, awesome-typescript-loader to do so, which are not included with CRA. Thus, you will have to install those manually:

$ npm install babel-loader awesome-typescript-loader

Additionally, while Create React App uses Babel to parse JSX, Storybook expects TypeScript to do so. Thus, create a Storybook-specific TypeScript configuration in .storybook/tsconfig.json that extends the one you already have with that setting, as follows:

{
  "extends": "../tsconfig",
  "compilerOptions": {
    "jsx": "react",
  },
}

Then, in .storybook/webpack.config.js, tell awesome-typescript-loader to use that configuration file:

    loader: require.resolve('awesome-typescript-loader'),
    options: { configFileName: path.resolve(__dirname, './tsconfig.json') }

You should now be good to go!

Thanks

The main driver behind adding TypeScript support to Create React App was Bruno Lemos, who worked very hard on his pull request. Of course, he could not have done that without the support of the Create React App team, and in particular Joe Haddad, who spent a lot of time reviewing the pull request - and also reviewed this blog post. And of course, we should be grateful to Will Monk, who maintained ~~(and is still maintaining)~~ his excellent fork of create-react-app that allowed us to use TypeScript when it was not officially supported yet.

License

This work by Vincent Tunru is licensed under a Creative Commons Attribution 4.0 International License.


ReactTypeScript