
How I rewrote our codebase to TypeScript in a week
How hard could it be? đ¤
As a newly recruited consultant on a Norwegian government project to digitalize the communication between individuals, the government and businesses I was greeted with a React app all written in JavaScript, and developers with more experience than me said it would take weeks or months to rewrite our app to TypeScript. I realized that many people think of TypeScript as a whole new language and not a typed superset of JavaScript.
TypeScript is JavaScript, only with types.
But what are types? How can it just be added to JavaScript?
But what are types? How can it just be added to JavaScript? Types define what kind of values an object, variable or constant can be assigned. JavaScript doesnât care about that, thatâs why TypeScript âencapsulatesâ your code and gives you compilation errors when you assign a string to a variable that is defined with number type.
In the end, TypeScript is only for the developers. Since it gets compiled to JavaScript when youâre building the application.
But why do we need types?
You might ask yourself âBut if itâs only for me as a developer, do I need it?â. The answer is probably no if youâre the only one working on a small project. But at our project, we are over 10 consultants working on the same codebase. If you think of it and you come over a function like this
export function add(a, b) {
return a + b;
}
It does seem like a simple function, right? It adds two values and returns it. So youâre going to use this function with two parameters that you get from another function.
import { add } from './utils';
function getAndAddValues(a, b) {
let sumOfValues = add(a,b);
}
What happens if your colleague sends in an object to âgetAndAddValuesâ? Where is the warning when youâre only using JavaScript? Itâs no warning, itâs a bug, and itâs out in production.
TypeScript wouldnât compile this piece of code if youâve typed your code correctly. And then you wouldâve saved your team a bug in production.
So, how do I rewrite my application to TypeScript?
When I write ârewriteâ itâs not to start from scratch, because I said that TypeScript is JavaScript, just with types right?
The most time-consuming job is actually to rename all your JavaScript files (.js/.jsx) to TypeScript files (.ts/.tsx):
App.jsx -> App.tsx
If youâd do that right away you might run into bundling errors with Webpack. Because you havenât defined any rules for TypeScript files in your âwebpack-config.jsâ-file.
If youâre using âCreate React Appâ you might have to eject your app for this. Or rewire your app with react-app-rewired.
My opinionated recommendation is to use awesome-typescript-loader but there are other options such as ts-loader.
Follow the steps for the TypeScript loader of your choice. And youâre set. Or are you? Youâll need one more file in the root of your project. Youâll need a âtsconfig.jsonâ-file.
This file is the configuration of typescript for your project, it can be very strict or not so strict. Hereâs the content of a basic configuration of TypeScript in tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"outFile": "./dist/index.js",
"sourceMap": true
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
"**/*.spec.ts"
]
}
Read more about the settings you can define in this file here.
So, you think youâre all set now? Not quite. Youâll need typescript installed. Thatâs as simple as a:
npm i typescript -D
And now, your files will be compiled. Or at least webpack will compile if you donât have any errors, based on a basic application youâll have a few errors. Mostly that you are missing typings for the dependencies your app has defined in package.json.
So, you got me to break the whole project? What now?
This is where I leave you⌠Iâm just kidding, Iâll hold your hand through this transition. So, youâll need typing-packages for your dependencies. Iâm guessing you have react and react-dom as dependencies? Then you need:
npm i @types/react @types/react-dom -D
The â@types/â prefix installs a package in a folder called @types in your node_module folder. It is used by TypeScript to check types when you are importing and using external packages. Almost all of the packages you use has a typings-package prefixed with @types/{package-name} . Some packages even supply own types, which is cool.
So now all your external typings from dependencies are installed and youâre ready to write some of your internal typings.
Letâs get going! This is why youâre here! (I think đ¤)
Letâs start with typings for variables and constants.
// from this
let name = 'Jesper';
// to this
let name: string = 'Jesper';
You can define variables or constants as a string, number, boolean or any. (Donât use any, itâs like saying you like ice cream, but you like it warm⌠But seriously itâs like youâre trying out TypeScript without types, why?).
Letâs see, how do you write typings for objects? Easy, you declare your interfaces. Letâs do a simple person-example.
interface IPerson {
firstName: string;
lastName: string;
age: number;
isCool: boolean;
}
const person1: IPerson = {
firstName: 'Jesper',
lastName: 'Førisdahl',
age: 25,
isCool: true,
};
You define the keys the object will have, and the type of value they are expecting to be.
"But Jesper, this is all good⌠But how do I use TypeScript with React?"
Yes, Iâm talking to myself in third-person. Deal with it.
Easy. Like this:
import React from 'react';
// Declare types of props
interface IProps {
text: string;
}
// Declare types of state
interface IState {
text: string;
}
// Old stateful declaration
export class TypedComponent extends React.Component<IProps, IState> {
constructor(props: IProps) {
super(props);
this.state = {
text: props.text,
};
...
}
...
render(): JSX.Element {
return (
<div>
{this.state.text}
</div>
);
}
}
// New declaration with hooks
export function TypedComponent(props: IProps) {
const [text, setText] = React.useState(props.text);
return (
<div>
{text}
</div>
);
}
Probably a little harder than me just saying âeasyâ. But with the old way of declaring components with classes you need to provide the typings for the props and state in the <âŚ> in that order.
But what about optional values in an object?
Itâs as simple as adding a `?` after the key name.
interface IProps {
text: string;
additionalText?: string;
}
So, now you have a basic setup and introduction to TypeScript, do you feel confident enough to rewrite your project?
At our project, we use redux with redux-saga, so if you are interested in an article in this format about how we write typings for this, comment below! đ