TypeScript And React: 7 Lessons Learned

One of the first programming languages I learned was Java. I liked it back then but always found it a bit overcomplicated (hello, FileInputStream). After a while I completely transitioned to a full JavaScript-only stack using React and Node and I loved it (and still do) because there was no nagging compiler and I never stumbled over lots of typecasting issues. Of course this flexibility means that you certainly require a decent amount of discipline while writing code but being half German/Japanese – no problem with that.

via GIPHY

Many years later I noticed that the voices celebrating TypeScript became louder and louder. I’ve always been thinking: Wouldn’t it be cool if JavaScript was strictly typed (without the complexity of Java) and could be ran natively in the browser? Well, we’re not there yet with the latter (Deno already is) but after struggling a lot with maintenance in a larger project lately I decided to give TypeScript a try and port one of my smaller React apps into TypeScript-only.

There are plenty of tutorials about this topic on the web. One of them being Mark Pollman’s excellent article “Migrating a React Codebase to TypeScript“. However, in this post I want to give you insight into my journey throughout an afternoon of porting jsx into tsx and there is certainly one thing I can tell you ahead: It ain’t that easy.

1 Defining Props

As we’re now strictly typed you need to define for each prop what type it is. Here is how:

import React from 'react';

type Props = {
  foo: string,
  // Add more props here
};

export default (props: Props) => {
  return <div>{props.foo}</div>;
}

2 Standalone Transpiling

If you want to transpile a bunch of tsx files into regular ES5 JavaScript you need a couple of extra steps.

IMPORTANT: This is not required if you just want to use TypeScript in a create-react-app!! Instead, I used the following approach to transform jsx code directly into plain es5 using Babel transpiler in a different project:

First, install dependencies:

npm install @babel/plugin-transform-typescript @babel/plugin-transform-react-jsx @babel/preset-typescript @babel/preset-env --save-dev

Then, add a new script to package.json:

"dist": "babel --plugins=@babel/plugin-transform-typescript,@babel/plugin-transform-react-jsx src --out-dir dist --extensions '.ts,.tsx'"

Finally, create a new file called babel.config.js and add:

module.exports = {
  presets: ['@babel/preset-typescript', '@babel/preset-env'],
};

3 Do not mix tsx, jsx and js files

Seriously, don’t do it

via GIPHY

4 Importing images

The following will throw an error that TypeScript cannot find the module although the image file clearly exists:

import img from './img.jpg';

To fix this add the following to your tsconfig.json. If you don’t have a tsconfig.json file yet, follow this tutorial to create one.

"include": ["src", "./index.d.ts"]

Then, create another file index.d.ts and add:

declare module '*.jpg';

5 Fix missing declaration files

When using libraries not written in TypeScript you will have to import a declaration file in order to stop the compiler from nagging. If not, you will encounter an error like this while XXX is the name of the library/dependency.

Could not find a declaration file for module ‘XXX’. ‘/Users/foo/proj/node_modules/XXX/lib/index.js’ implicitly has an ‘any’ type.
Try npm install @types/XXX if it exists or add a new declaration (.d.ts) file containing declare module ‘XXX’;

TypeScript Compiler Error

To fix this, you can either search for your required d.ts file manually under the following URL (while XXX is the name of your lib):
https://www.typescriptlang.org/dt/search?search=XXX

Or save yourself some headaches and do this:

  1. npm install --save-dev @types/node
  2. Add "noImplicitAny": false to tsconfig.json

6 Is it worth it?

Absolutely. After a couple of minutes of playing around TypeScript immediately catched errors and the best part is you won’t even have to define that many types. TypeScript is pretty good at guessing what you want. Here is an example with an array which is defined in a separate file called date-dic.tsx:

export default [ 'January', 'February', 'March' ];

As you can see, this is an array of strings. So let’s hop to a different file and import the array:

import date_dic from './date-dic';

date_dic.forEach((el, i) => {
let x = el / 3;
});

TypeScript will throw an error in this line: let x = el / 3;

(parameter) el: string
The left-hand side of an arithmetic operation must be of type ‘any’, ‘number’, ‘bigint’ or an enum type

TypeScript Compiler Error

The reason is that TypeScript implicitly knows that each element in the array is a string and that we’re trying to apply an arithmetic operation on it which doesn’t make any sense. Pretty cool.

7 Pro Tip: Enable auto-formatting with Prettier in VSCode

Follow my tutorial here in order to get the magic working

Follow me on social media

Leave a Reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.