TypeScript Best Practices 2021

Warsha Kiringoda
6 min readAug 18, 2021

--

programmer celebrating good code

TypeScript(TS) has proven to be a better option to write client side scripts without using JavaScript (JS). And to write it in a more cleaner way. To make the most of it, it is important to follow the best practices when coding with TS.

Here are 11 TypeScript best practices that I follow and recommend.

1. Use correct data type annotation ( avoid ‘any’ )

Data type annotation is one of the advantages you have when coding in TS, while JS defines data types in the run time. defining data types in TS saves you from weird run time errors that can occur by mistakes. Do not use ‘any’ keyword when you know what type of data your variable’s gonna hold. It is recommended that you always define the data type whenever you declare a new variable.

name: string = “Hello”;value: number = 50;isCorrect: boolean = false;

2. Enable strict check on

‘use strict’ feature was added to JavaScript in ES5. It has the literal meaning which is “the code should be in strict mode”. In TS code you can find the ‘strict’ configuration in the tsconfig file.

‘strict’ value set to true
configure “strict” in tsconfig file

This restricts you from doing unintentional or silly mistakes like using an undeclared variable, not using type annotations, or trying to use a future reserved keyword as a variable name, etc. ‘use strict’ helps you write good and secure code by showing bad coding habits as syntax errors.

3. Use ‘let’ instead of ‘var’

var is your good old friend while let came into the picture in ES6 version. let and const were introduced to reduce some drawbacks TS had with var.

var is either a global scope or a local scope declaration. A var type variable becomes a globally scoped variable when it is defined outside a function/block. In that case the variable is available to be used anywhere inside your script. var becomes locally scoped when it is defined inside a function. In those cases it is only accessible inside that function.

var name= "John Doe"; // global scope variable
function getAge() {
var age= 30; // local scope variable
}

There are several drawbacks of var. It can be redeclared, can be called without declaring it. TS won’t show any error but you will end up with surprising outputs.

var name = "John Doe";
function getName(){
var name = "Anne"; // no error is showed
}

To avoid this, you should use let instead. let is a blocked scope variable declaration. And you cannot re-declare it. But you can declare the same variable name in different scopes, and each of them will be treated as separate different variables.

let name = "John";
if (true) {
let name = "Anne";
console.log(name); // "Anne"
}
console.log(name); // "John"

4. Use ‘const’ for constants

const came in to the picture together with let. const is also a blocked scope type. Also we cannot re-declare a const. These are the similarities between let and const.

However the purpose of const comes with the feature that it’s value cannot be updated either (We could update with let). So always use const when you declare a constant.

const name = "John";
name = "Anne";// error
const age = 30;
const age = 31; //error

P.S: When declaring const objects, you cannot update it. But you can update it’s properties.

5. Use tuples for fixed length arrays

let marks: number[] = [1, 2, 3];

You can use the above ‘marks’ array to store different number of items in different places of the same script. TS is not gonna restrict you as long as you provide all the values with the correct defined data type.

let marks: number[] = [1, 2, 3];
marks = []; // success
marks = [1]; // success
marks = [1, 2, 3, 4, 5]; // success

However this can lead to nasty logical errors in cases where the array length is a constant. To avoid these you should use array as a tuple, whenever the size should be a fixed size. Tuple is a properly defined array with each of the expecting value’s data type.

let marks:[number, number] = [1, 2]; // tuple of 2 number values
marks = [10, 20]; // success
marks = [1]; // syntax error
marks = [1, 2, 3, 4, 5] // syntax error

6. Use type aliases in repetitive data types

Assume that you have multiple variables/ objects in your script which all follow the same structure of data types.

let man: {name: string, age: number} = {name = "john", age=30};
let woman: {name: string, age: number} = {name = "Anne", age=32};

to avoid this redundant chunks of type declarations and to re-use types, you can use type aliases.

type Details = {name: string, age: number}; // defining type alias
let man: Details = {name = "john", age=30}; // using type alias
let woman: Details = {name = "Anne", age=32};

The additional benefit of using a type alias is, your intention of defining the data is now clearly visible.

7. Decide between ‘any’ and ‘unknown’

any and unknown does the same help if you look from the surface. They help you to easily refactor JS to TS when JS gives you no clue about the data types. In these kind of scenarios or when you don’t really know what type of data to expect, any and unknown comes to help. But there is a difference.

You can use whatever value with both any and unknown type variables.

let anyExample: any; // defining an any
let unknownExample: unknown; // defining an unknown
anyExample = 123;
anyExample = "Hey"
unknownExample = false;
unknownExample = 23.22;

However, you can do anything with ‘any’, when it comes to calling functions of that variable.

anyExample.you.made.this.code.chain(); // success

But you can’t do this with unknown. unknown is safer.

unknownExample.trim(); // syntax error

If you wanna use unknown variable, you have to do it inside a condition block.

if (typeof exampleUnkown == "string") { // 1st check the type
exampleUnkown.trim(); // No syntax error now
}

8. Use access modifiers for class members

TS comes with access modifiers for properties of a class, while classes are always public. You can create public, protected or private properties.

  • private: only accessible inside the class
  • protected: only accessible inside the class and through subclasses
  • public: accessible anywhere
class Employee {
protected name: string;
private salary: number;

constructor(name: string, salary: number) {
this.name = name;
this.salary = salary
}

public getSalary(){
return salary
}

Here, you cannot access the salary unless you use the getSalary method.

class Developer extends Employee{  viewDetails(){
console.log(this.salary); // error: property 'name' is private
console.log(this.getSalary()); // success
}
}

But you can access the name through a sub class.

class Developer extends Employee{
viewDetails(){
console.log(this.name);
}
}

9. Avoid unnecessary comments

Commenting is good as long as it is absolutely necessary. Always try to use intuitive names when naming variables and functions. It will reduce the necessity for you to add comments. Do not comment out source code lines. In debugging it is acceptable but keep in mind not to push commented out source code.

10. Use a Linter

It is important to use a lint tool if you don’t want to pollute your codebase. This is specially important when you’re working in a team. Different developers have different coding habits. Therefore a linter should be used so that all the developers unintentionally agrees to the same set of rules. When it comes to linters the go-to option is ESLint. It is compatible with both JavaScript and Typescript.

11. Use a code formatter

Using a good code formatter makes your coding more efficient and cleaner. From my personal experience I prefer using Prettier in VS code . But there are many code formatters out there and the choice depends on you and the editor you use.

Good codes make happy coders!

--

--