DEV Community

Timo Ernst
Timo Ernst

Posted on • Updated on • Originally published at

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.

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>{}</div>;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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'"
Enter fullscreen mode Exit fullscreen mode

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

module.exports = {
  presets: ['@babel/preset-typescript', '@babel/preset-env'],
Enter fullscreen mode Exit fullscreen mode

3 Do not mix tsx, jsx and js files

Seriously, don’t do it

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';
Enter fullscreen mode Exit fullscreen mode

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"]
Enter fullscreen mode Exit fullscreen mode

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

declare module '*.jpg';
Enter fullscreen mode Exit fullscreen mode

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’;
Enter fullscreen mode Exit fullscreen mode

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):

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' ];
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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

Read my tutorial here in order to get the magic working

Follow me here on social media:

Top comments (2)

alekseiberezkin profile image
Aleksei Berezkin

Thanks for sharing! How exactly a trick with images works? Is there some bundler which actually loads it for you? Afaik if you just outsmart TS compiler here, img will be undefined.

timo_ernst profile image
Timo Ernst

I don’t really know how it works under the hood but it solved the issue for me :-)