Converting existing JS to Typescript
In the ever-evolving landscape of web development, maintaining scalable and maintainable code is crucial. TypeScript emerges as a powerful solution to address the challenges posed by the dynamic nature of JavaScript. Typescript adoption has exploded in the last few years and for a very good reason.
With a syntax that closely resembles JavaScript, TypeScript seamlessly integrates with existing projects and libraries, offering a smooth transition for developers familiar with JavaScript. Not only does it improve code reliability and collaboration in large-scale projects but it also enhances the developer experience by providing advanced tooling, such as intelligent autocompletion and rich code navigation. TypeScript’s compatibility with popular frameworks like React, Angular, and Vue.js opens up new possibilities for building modern web applications with confidence.
With that said some of the concepts can be hard to grasp at first. For example generics have a special syntax to initialize them and are not very intuitive. keyof type, indexed access, extends can also be a little tricky to learn.
What i want to do now is walk through a few conversion examples for us to get a clearer picture of how this looks in real life. Here is the sandbox for a simple weather fetcher application i created. We will try to convert it step by step to a typescript app. Note this is only a single page front end app, but similar steps can be taken on the server side.
- First we will need to install typescript, to do so run the following on your IDE terminal.
npm i typescript
- Next generate the typescript file with the compilation options
npx tsc –init
Once the tsconfig.json file is generated enable these options:
allowJs: true => This is so that the current js files are accepted as is and we can change them incrementally as needed.
include: [“src/**/*.ts”, “src/**/*.tsx”] => This specifies which files we want to transpile to plain javascript. (In this case every ts file under src)
exclude: [“node_modules”] => This specifies which files to exclude, typically only used if include is missing.
noImplicitAny: true => To ensure that any is not an acceptable value a type, otherwise there is value in using typescript at all.
isolatedModules: true => To ensure each file can be transpiled without relying on other imports.
jsx: “preserve” => This specifies the JSX used for representational components (react)
- If working on a react project your will need to import types for react libraries as follows:
npm i @types/react @types/react-dom
This applies to any libraries you are importing, for example if working with node (on the server side) you would install the node type packages as follows:
npm i @types/node
- If everything up to this point does not cause any issues with starting the server simply start converting the files (js => ts, jsx => tsx). Here is converted application. This an example of a converted file.
Before:
export const cToF = (celsius) => {
var cTemp = celsius;
return (cTemp * 9) / 5 + 32;
};
export const fToC = (fahrenheit) => {
var fTemp = fahrenheit;
return ((fTemp - 32) * 5) / 9;
};
export const getTempteratureColor = (temp) => {
if (temp < 60) {
return "blue";
} else if (temp >= 60 && temp <= 85) {
return "green";
} else {
return "red";
}
};
export const debounce = (func, timeout = 300) => {
let timer;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
};
After:
export const cToF = (celsius: number) => {
var cTemp = celsius;
return (cTemp * 9) / 5 + 32;
};
export const fToC = (fahrenheit: number) => {
var fTemp = fahrenheit;
return ((fTemp - 32) * 5) / 9;
};
export const getTempteratureColor = (temp: number) => {
if (temp < 60) {
return "blue";
} else if (temp >= 60 && temp <= 85) {
return "green";
} else {
return "red";
}
};
export const debounce = (
func: (...args: any[]) => void,
timeout: number = 300,
): ((...args: any[]) => void) => {
let timer: NodeJS.Timeout;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
};
// or if we wanted to use an interface for func
// we can do something like this using generics
export const debounce = <T extends (...args: any[]) => void>(
func: T,
timeout: number = 300,
): ((...args: Parameters<T>) => void) => {
let timer: NodeJS.Timeout;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => {
func.apply(this, args);
}, timeout);
};
};