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>{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
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.
TypeScript Compiler Error
Try npm install @types/XXX if it exists or add a new declaration (.d.ts) file containing declare module ‘XXX’;
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:
npm install --save-dev @types/node
- 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
TypeScript Compiler Error
The left-hand side of an arithmetic operation must be of type ‘any’, ‘number’, ‘bigint’ or an enum type
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