Upgrading a React and TypeScript app from react-scripts-ts to Create React App v2.1
Tags: react, typescript, createreactapp
Published: 2018-11-29 15:53:54

---
title: Upgrading a React and TypeScript app from react-scripts-ts to Create React App v2.1
published: true
tags: React,TypeScript,Create React App
canonical_url: https://joshblog.net/2018/upgrading-a-react-and-typescript-app-from-react-scripts-ts-to-create-react-app-v2-1/
---

Recently, the React team updated [**Create React App**](https://facebook.github.io/create-react-app/) with [official support for **TypeScript**](https://facebook.github.io/create-react-app/docs/adding-typescript). Create React App makes it easy to create web apps with React by keeping the details of the build process out of your way. No need to configure Webpack, Babel, and other tools because Create React App offers very high quality defaults. It’s not a boilerplate, though. You don’t even need to see the build dependencies and scripts, unless you want to, and upgrading to new versions is super easy. [TypeScript](http://www.typescriptlang.org/) is a popular extension to JavaScript that adds compile-time type checking. The two combined, in my opinion, provides one of the best developer experiences you can get for modern frontend web development.

Previously, to use TypeScript with Create React App, you had to create your project with the custom [_react-scripts-ts_, created by Will Monk](https://www.npmjs.com/package/react-scripts-ts). Starting in version 2.1 of Create React App, TypeScript works out of the box. I have several projects that were started with _react-scripts-ts_, and I recently decided to start upgrading them to Create React App v2.1. I thought I’d share the steps that I followed and my experiences with this process.

### Create a new baseline app

To start, I decided to create a completely new app with Create React App. I figured that it would be a good idea to have a baseline app, where I could compare its files — like _package.json_ and _tsconfig.json_ — with the files from my existing projects. This new app would give me clues about what exactly I need to change to make the existing apps work properly with the new version of Create React App.

To create a new app, I ran the following command in my terminal:

``` sh
npx create-react-app my-new-app --typescript
```

### Update library dependencies

In the existing project that I was upgrading, some of my dependencies hadn’t been updated in a little while, so the next step I decided on was to update the _react_, _react-dom_, _typescript_ dependencies, along with the type definitions, like _@types/react_, _@types/react-dom_, _@types/node_, and _@types/jest_. After updating the versions in _package.json_, I ran `npm install` to download them all.

**I left _react-scripts-ts_ untouched for now.** The idea was to make sure that the existing project was using the exact same versions of the dependencies as the new baseline project, and it should still build properly with _react-scripts-ts_. It’s best to ensure that I wouldn’t run into any issues with new versions of React or TypeScript that might confuse me as a worked through any issues caused by the differences between _react-scripts-ts_ and the official _react-scripts_.

I needed to make a couple of minor tweaks to so that the new version of the TypeScript compiler was happy, so it was a good thing that I took this extra step.

### Switch from _react-scripts-ts_ to _react-scripts_

Once all of my library dependencies matched and the project would still build with _react-scripts-ts_, it was time to switch to the official _react-scripts_. Inside _package.json_, I replaced the _react-scripts-ts_ dependency with _react-scripts_. I copied the version number of _react-scripts_ from my new baseline project. Then, I needed to update the npm scripts inside _package.json_ to use _react-scripts_:

``` json
"scripts": {
  "start": "react-scripts start",
  "build": "react-scripts build",
  "test": "react-scripts test",
  "eject": "react-scripts eject"
}
```

I replaced _react-scripts-ts_ with _react-scripts_ in each of the npm scripts above. Then, inside the **build** script, I saw that my existing project included a `--env=jsdom` option, but the new baseline project did not. I removed this option so that they matched.

### Update configuration files

Next, I compared _tsconfig.json_ in both projects. I could see some major differences, like outputting ES Modules instead of CommonJS, and preserving JSX so that Babel could handle it instead of letting the TypeScript compiler do everything. I decided that I would simply copy the contents of _tsconfig.json_ from the new baseline project to the existing project and then make a couple of small tweaks. I ended up setting the `lib` compiler option to `["es2015", "dom"]` so that I could access some newer JS APIs, and I set the `strict` compiler option to `false` because I haven’t updated my project to use strict mode yet. Otherwise, I used Create React App’s defaults, which all seem to be working well.

Similarly, I compared _package.json_ in the two projects. I had already updated the dependencies and npm scripts, so the differences at this point were very minor. I saw that the new baseline project included two new fields that were not in the existing project, `eslintConfig` and `browserslist`. These defaults (which configure eslint and Babel) provided by Create React App seemed fine, so I copied them over.

In theory, the migration process should be done at this point. If all goes well, you should be able to run `npm start` to launch the development server, or you can run `npm run build` to create a production build. For my project, I ran into a couple of issues that I needed to resolve, but nothing major.

### Fix compiler errors

I use the [_react-loadable_](https://jamie.build/react-loadable.html) library to load parts of my app at runtime using code splitting. My imports in TypeScript originally looked like this:

``` typescript
import * as ReactLoadable from "react-loadable"
```

However, there seemed to be a problem with the following code:

``` typescript
let AdminHomeScreen = ReactLoadable({
  loader: () => import("./AdminHomeScreen"),
  loading: LoadingScreen
});
```

The TypeScript compiler gave me the following error:

> Cannot invoke an expression whose type lacks a call signature. Type '{ default: Loadable; Map&lt;Props, Exports extends { [key: string]: any; }&gt;(options: OptionsWithMap&lt;Props, Exports&gt;): (ComponentClass&lt;Props, any&gt; &amp; LoadableComponent) | (FunctionComponent&lt;Props&gt; & LoadableComponent); preloadAll(): Promise&lt;...&gt;; preloadReady(): Promise&lt;...&gt;; Capture: ComponentType&lt;...&gt;; }' has no compatible call signatures.

For some reason, I could not call the `ReactLoadable` module as a function. It turned out that I needed to change the import to look like this instead:

``` typescript
import ReactLoadable from "react-loadable"
```

In order to call a module like a function, you need to import the module’s _default export_. I think this may have been related to switching from CommonJS modules to ES Modules in _tsconfig.json_.

After fixing this issue, I could successfully run `npm start` to launch the development server, and I could open my app in a web browser and use it! However, I discovered one last issue that I needed to fix.

### Fix production build

While `npm start` worked for development, I got a strange error when I ran `npm run build` to create a production build of my React app.

> module.js:550  
> throw err;  
> ^
> 
> Error: Cannot find module ‘webpack/lib/RequestShortener’  
> at Function.Module.\_resolveFilename (module.js:548:15)  
> at Function.Module.\_load (module.js:475:25)  
> at Module.require (module.js:597:17)  
> at require (internal/module.js:11:18)  
> at Object.<anonymous> (C:\Users\josht\Development\karaoke-butler\karaoke\client\node_modules\terser-webpack-plugin\dist\index.js:19:25)<br>
> at Module._compile (module.js:653:30)<br>
> at Object.Module._extensions..js (module.js:664:10)<br>
> at Module.load (module.js:566:32)<br>
> at tryModuleLoad (module.js:506:12)<br>
> at Function.Module._load (module.js:498:3)<br>
> npm ERR! code ELIFECYCLE<br>
> npm ERR! errno 1<br>
> npm ERR! react-client@1.0.0 build: `react-scripts build`<br>
> npm ERR! Exit status 1<br>
> npm ERR!<br>
> npm ERR! Failed at the react-client@1.0.0 build script.<br>
> npm ERR! This is probably not a problem with npm. There is likely additional logging output above.</anonymous>
> 
> npm ERR! A complete log of this run can be found in:  
> npm ERR! C:\Users\josht\AppData\Roaming\npm-cache\_logs\2018-11-25T01\_57\_14\_654Z-debug.log

Unfortunately, a quick Google search did not find anyone having the same issue with Create React App. However, I saw some folks using other frameworks, like Vue or Angular, were getting similar errors. Eventually, I found someone who discovered that it was some kind of conflict inside the notorious _package-lock.json_. Ever since npm started using this file, it’s been a terrible source of problems, in my experience.

Anyway, I deleted _package-lock.json_ and the _node\_modules_ folder. Then, I ran `npm install` to install all of my dependencies from scratch. That did the trick because `npm run build` started working correctly.

I don’t really know why _package-lock.json_ always seems to cause problems like this, but I find that deleting this file and the entire _node\_modules_ folder frequently fixes issues that I run into. I don’t know if I’m doing something wrong, or if it’s a bug in npm.

### My project is upgraded

Updating dependencies in some of my web projects can be daunting sometimes. For some of them, I usually need to set a full day aside to fix everything. With that in mind, I was expecting to hit a few more snags in the process of upgrading from _react-scripts-ts_ to the official version of Create React App. However, the changes required in my app were relatively minor. The process felt very smooth, and I’m happy that I’ll be able to easily update to new versions of Create React App in the future as the React team continues to innovate.