August 6, 2021The good recruitment experience
August 11, 2020Handling promises in Sagas
August 11, 2020Modern Frontend Architecture Part 1: API
May 5, 2020Frontend Pattern: Route Object
May 1, 2020Frontend skills for the Web Designer
April 10, 2020Learning Frontend Path
January 26, 2020Podcasts I listen to (Jan 2020)
January 23, 2020Are you too old to start programming
January 22, 2020Use tabs not spaces
October 18, 2019Refactoring Overgrown React Components slides
October 2, 2019Book Review: Factfulness
October 1, 2019Yes, it's a good time to add TypeScript to your project
September 24, 2019Code Review – Best Practices, Guidelines & Process Insights
June 10, 2019Why I read on the iPad
March 18, 2019Software & Hardware I use
March 16, 2019Why I dont use React Router Link component
November 15, 2018Why public money software is not open source?
July 5, 2018How learning Angular made me better React developer
May 23, 2018My 12 tips how to increase your frontend coding productivity


Yes, it's a good time to add TypeScript to your project 🥰

I’m a TypeScript evangelist and I often ask if people use static type checking in their projects. Although TypeScript is getting more and more popular (2019 „the TypeScript year” woo-hoo), there are a lot of developers who seem to be afraid. Why? Of course, every major technical decision in the project should be evaluated against time, resources, costs and developers skills. We don’t switch React to Angular or Angular to React „because”, but we evaluate technologies and their costs. we also don’t want to introduce new technology to production application if we don’t have developers with at least some experience to be sure they can handle the job.

I don’t want to raise any protests here – of course you will not switch from Flow, because it still works. Of course you don’t introduce TypeScript if you have fire in the project. Let’s not discuss the business-cost-money here and focus only on technology.

📈 TypeScript yields more then you think and costs less then you think

TypeScript is far more than marking string or number everywhere when you might think it’s obvious and obscure the code.

TS is more mature version of JS and we can see how TS reaches to languages like C# for inspiration. It gives you extra features, but of course some of them you can provide by babel. Typescript allows JS proposals stage-3 and up.

TS is scalability. TS is safety. TS is speed.

Declaring types in your app allows you to scale your app. You can’t expect complex code to be maintainable when it grows and ages, without having type definitions. Many developers use JSDoc, trying to simulate what types give them out of the box.

TypeScript also is safety. No need to write dumb tests like it returns string type, because type checker do this for us, and we can focus on business and serious things!

How about speed? Newbie developers might think that extra syntax requires extra time overhead. I agree in case of very difficult types, but honestly – it’s a problem mostly for libraries’ creators who need to type them very generic-way. Typescript will slow you down a little at the beginning, but it will repay in first refactor when you don’t tear you hair out looking for this one small typo you made.

Let’s look at a few examples.

👥 Type aliases

type IsoDate = string;

Type aliasing gives you semantic meaning. Technically it’s string, but you often use iso date strings to store serialized dates (e.g Redux). With type alias code is self-explaining

🔑 Interfaces

interface WithBehaviour {
	doSomething(arg: string): Promise<string>;

Interfaces not only help you keeping Single Responsibility and Interface Segregation principles. They declare behavior, which can be shared easily across the code. It helps with composition and delegation, it also allows reasonable dependency injection.

interface Iterable {
	map<U>(callbackfn: (value: T, index: number, array: T[]) => U): U[];
	filter(callbackfn: (value: T, index: number, array: T[]) => unknown): T[];
	forEach(callbackfn: (value: T, index: number, array: T[]) => void): void;

This is more real example of how interface can share behaviors. In this case we can declare „Iterable” interface which shares common iteration methods know from arrays. Now you can construct e.g data collections which implements this interface. You are sure you can inject them to all services which iterate on data like in another example:

interface CanPrintCollection {
	print<T>(collection: Iterable<T> | Array<T>);

Now you communicate via interfaces, not implementations.

📂 Data structures

type CatsReducer = {
	catsById: {
		[id: string]: {
			name: string;
			age: number;
			favorite: boolean;

With types (and interfaces) you can define how objects look like. In this example I typed some piece of Redux store. Thanks to this typing I am sure my reducer operations will be almost bulletproof – TS will not allow me to set „true” to boolean field of make a typo. Everything will be highlighted and IDE will auto-complete my object accessing.

export type Cat = {
	name: string;
	age: number;
	favorite: boolean;

type CatsReducer = {
	catsByID: {
		[id: string]: Cat;
const store: CatsReducer;

const someCatName = store.catsByID['azor'].name; // Fine
const catsArrayBad = store.catsByID.forEach(/* ... */); // Error - its not an iterable
const catsArrayOk = Object.values(store.catsByID).map(/*...*/); // Fine - converted to array

You can also create more types in types and share them. Here our Cat is used in reducer, but it’s exported too. Notice that type checker will secure your from accidental operations like forEach on object. It will also know what type is argument in „map” callback. Great!

import { Cat } from 'cat.ts';

const createCat = (name: string, age: string, favorite = false): Cat => ({

We can import exported type all around our app and use it, safe from refactor. You can declare types of function arguments and also return values. How does it scale? Imagine you need to change Cat „name” to „displayName”. It should happen automatically (in WebStorm at least), and event if not – TypeScript will not compile unless you fix all old occurrences.

👨‍👨‍👧‍👧 Enums

enum Breed {

interface HasBreed {
	breed: Breed;

const setBreed = (animal: HasBreed, breed: Breed) => {
	return {

Enums releases you from typing strings by hand. Similar effects can be achieved with JS objects, but Enums also keep type values. Great for mapping possible fields, options etc. It's also better optimized for change - if you assume something is boolean (british: true/false) you might get surprised when business requirements change painfully.

✅ And more

These examples are only a tip of a huge iceberg of TS power. This article is not TypeScript showcase, but you can learn more in the docs.

I want to show you how easy is TS to start and how huge benefits it brings.

😱 Why developers are scared of TypeScript?

Many developers seem to be afraid of TypeScript. They have their old habits and don’t want to stress. We might think that devs are always eager to learn new things, but they are human like any other, some of them want to develop their skills, some of them are happy with 9-to-5 jobs.

The problem also may have it roots in maturity of javascript world. TypeScript isn’t new tech (released 2012), but ecosystem like Babel, Webpack, modules etc is still complex. Language and its tooling evolved, making it easier to use in time.

Also very popular VS Code is written with TS in mind (and in its source code too), so people can easily see benefits now, but once it was not that easy – required paid IDEs or settings plugins.

Another thing is learning curve. I think it’s quite easy – I don’t have a CS degree, where you learn „serious” languages, but I don’t think basic concepts like interfaces or enums are hard to get. Many developers did learn C# or Java on university, so it should be easier for them too (but not for Bootcamp „grads” though). I think there are far more difficult things in programming like RxJS or functional programming itself, which are very hard to start with, while TS is quite easy to start and it becomes hard when you have to type some generic utilities like mentioned RxJS.

🙌 Creating TypeScript apps are easier then ever

Today creating new TypeScript apps are as easy as pure JS.

In React you only do create-react-app --typescript and you are set up.

With libraries you can use one of toolkits like tsdx.

Angular is written in TS by default.

Non-framework apps are as easy as creating simple Babel config like here, use tsc CLI interfaces which comes with TypeScript or use ts-node to compile node.js on the fly.

Many libraries and starters are now TypeScript-first so you can always bootstrap them with proper settings.

🎠 Adding TypeScript to existing project is easier then ever

However one thing is creating new project, another is introducing it to existing, maybe legacy code. Is it possible?

If you don’t have very specific, custom build environment, it should be quite smooth.

Most of projects use Babel to transpile their JS to older versions. Since Babel 7 you can add one preset to compile TS to JS just like ES6 to ES5.

How about compatibility with existing JS files? TS is backwards compatible, so only thing you can lose here is not having types checking for code written in pure javascript. That’s why TS ask you to download type definitions if you import an NPM module without built-in types. But event if these types don’t exist (which is very rare!) you can still declare this module as any and use it on your own risk.

TS comes with tsconfig file and this is where you find 4 rules which help you starting with TypeScript:

	"checkJs": true,
	"allowsJS": true,
	"strict": false,
	"noImplicitAny": false

First two allow you to use JS along with your TS files. You don’t have to rewrite your app!

strict is flag which should be set to true, but it will be quite hard to keep it during rewriting. Disabling it can help maintaining compatibility, but you should aim to turn it on later. You can check its features here.

noImplicitAny tells TS to allow… not typing. With this flag disabled it will not throw error on code like const foo = (x) => x, while normally you should declare what is the type of x. You can type const foo = (x: any) => x which will be valid with this flag enabled, but its pointless – you will bloat your app without any value.

💪 Tips on refactoring

I don’t recommend to rewriting the app just to have TS because it’s cool. But developers often just refactor apps and rewrite modules anyway, so I recommend to try it there.

For example – I need to refactor some piece of the app, create new files etc.

Write tests on piece of app you are going to refactor Create public interfaces of this module. Declare exported functions, types, enums – anything you expose to other modules. Write your fresh code with TypeScript and make sure they implement interfaces properly. Make tests green. Another way is to move some module or API to npm package. It’s great way to scale app, by extracting it to small, independent libraries. Each of them can have its own build system, so you don’t have to worry about including TS config to existing codebase. Just write it in fresh environment and import to your code as a library. It’s a great start of rewriting the app, because if you do total re-write, you can still use well written packages!

Then, piece by piece and file by file, your app will become cleaner, safer and easier to maintain

Here you can read about adding TS to your existing project.