Typescript Tips and tricks for JS Developers (Part1)

Introduction 🚩:

Hello Dear Readers and followers, in this blog I will write about an interesting topic Typescript

This rich programming language with the coolest APIS and features and helps us to write better code => better solution => fewer Bugs

I will try to list some tricks and tips with code examples, benefits, and use cases

Happy reading :)

What is Typescript 📢 ?

Created and maintained by Microsoft, TypeScript is a superset of JavaScript, meaning that all functional JavaScript code is valid under TypeScript. The language can be understood as “JavaScript for application-scale development,” with two primary focuses:

Typescript Growth ⤴️:

Nowadays, Typescript becomes one of the fastest-growing languages in the world, and currently is the leading compile-to-JavaScript language

As mention in the images below, it describes the number of pull requests and projects made by Typescript,

Currently, it touches both sides (front-end and back-end) and used in so many projects in production

So it’s not worth to learn it and get some cool tips about it

Source:https://redmonk.com/jgovernor/files/2019/05/swift_typescript_kotlin.png

Typescript advantages 💰:

My advice always for junior developers to ask why choose this technology, why this programming language, what’s the benefits and the advantages that can offer,

And this is a list of some advantages offered by Typescript :

  • Object-oriented programming: TypeScript supports object-oriented programming .💎
  • Typing: TypeScript offers static typing ( Type safe = less errors)💎
  • Reducing bugs, by writing more explicit and understandable code💎
  • Pack more value into your code without reinventing the wheel.💎
  • Compilation: Unlike JavaScript, which is an interpretative language,💎 TypeScript compiles the code for you and finds compilation errors, which make it easier to debug.💎

Typescript Tips :

Now in this section, I will try to demonstrate for you the simplest tips that can be helpful during development, take notes ✍️!

#Tip1: Know the available types 🔧 🔨 :

- Enum Type

TypeScript enums allow you to define a set of named constants, and it has both: numeric and string-based enums.

Code-Example :

enum CarType {
Honda = "HONDA",
Toyota = "TOYOTA",
Subaru = "SUBARU",
Hyundai = "HYUNDAI"
}
// Access String Enum
CarType.Toyota; //returns TOYOTA
CarType['Honda']; //returns HONDA

Enums type advantage 📌 :

  • Allows for the creation of memory-efficient custom constants in JavaScript
  • Flexibility for typing and code documentation
  • We can keep our code DRY, easy to refactor, and more readable.

-Heterogeneous enums:

Typescript offers the advantage of defining, Heterogeneous enums for example combining Numeric type, and string types :

Code-Example :

enum BooleanHeterogeneousEnum {
Yes = 0,
No = "NO",
}

Advantage 📌 :

  • More flexibility in typing

- Computed enums

Typescript provides another feature while using type is “Computed Enum”

Code-Example :

enum CarType {
Honda = 1,
Toyota = getCarTypeCode('toyota'),
Subaru = Toyota * 3,
Hyundai = 10
}

function getCarTypeCode(carName: string): number {
if (carName === 'toyota') {
return 5;
}
}

CarType.Toyota; // returns 5
CarType.Subaru; // returns 15

Advantages 📌 :

  • Increase Performance

-Tuple Type :

A tuple is a TypeScript type that works like an array with some special considerations:

  • The number of elements of the array is fixed.
  • The type of the elements is known.
  • The type of the elements of the array need not be the same.

Code-Example :

// Declare the tuple
let option: [string, boolean];

// Correctly initialize it
option = ["uppercase", true];
// Incorrect value order
option = [true, "uppercase"];

Advantages 📌 :

We can benefit from the tuple type in some use cases, for example :

  • Returning more than one value from a function
  • Representing parameters of a function

- Type any :

anytype is very simple. We don’t know what type we’re dealing with, so it might as well be any.

Code-Example :

let person: any;person = "tets";person = false;person = 34;

When to use type Any📌:

type any should where there is no type definition available for the code your' re working on like (third-libraries for chart , or API that you don't know the reponse format )

Unions type: |

In TypeScript, we also use | to denote union types.

Code-Example :

type HttpMethod = "GET" | "POST" | "PATCH" | "DELETE";function xhr(method: HttpMethod, url: string): void {
console.log(`${method}: ${url}`);
}

When to use union | 📌:

Sometimes a type has only a few possible values, so we can also use union types to represent them.

The unknown TYPE

The unknown type h is the type-safe counterpart of the any type.

Code-Example :

let value: unknown;
value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = Math.random; // OK
value = null; // OK
value = undefined; // OK
value = new TypeError(); // OK
value = Symbol("type"); // OK

When to use unknown | 📌:

The unknown type can be used where we are dealing with variables that we didn’t know the type

Type Interface vs Class

With Typescript we can use Interface, the class for data modeling and typing, So I want in this section to explain the difference and when to use an interface or class!

Code-Example :

// Interface definitioninterface IEmployee {
empCode: number;
empName: string;
empDept?:string;
}
// Class definition class Greeter {greeting: string;constructor(message: string) {this.greeting = message;}greet() {return "Hello, " + this.greeting;}}let greeter = new Greeter("world");
  • A class is used for object creation that can be initialized, encapsulation for fields properties and methods
  • An interface essentially defines the properties and type an object can have

#Tip2: Safe code with optional 🔧 🔨

Optional notation: ?

Typescript provides the symbol ? that can be used for optional chaining and optional properties!

-Optional chaining: ?. ✅

If we are dealing with property access on nested objects. like in the example above :

const arr = [{ code: "a" },{ code: "b" },{ code: "c" },{ name: "Caty" },{ name: "Siri" }];const withCode = arr.map(function(element) {if (element.code) return element;});
const notThere = withCode[3].code;
console.log(notThere);// error TS2532: Object is possibly 'undefined'.

If the object does not have the code property, it will cause an error at runtime !

With Typescript we can protect our code from these types of errors :

Solution ☑️:

const notThere = withCode[3]?.code;console.log(notThere);

-Optional Properties ?:

If we are defining an interface for data modeling , any variable of type in this interface should have all the fields!

it will cause an error field Ais missing in type X

interface User {name: string;email: string;}
let user1: User = {name: "Ahmed",};
// Property 'email' is missing in type '{ name: string; }' but required in type 'User'.

Solution ☑️:

The solution is to add ? for properties definition that will make them optional and required and offers for us more flexibility while defining our objects

interface UserWithOptional {name?: string;email?: string;}let user2: UserWithOptional = {name: "Ahmed",};

#Tip3: Don’t repeat yourself with TypeScript — Generics 🔧 🔨 :

This tip will talk about Generics in Typescript that can be implemented through Generic Constraints, Generic Classes, and Generic functions which helps us to write reusable and modular code

Generic functions :

Problem :

Sometimes you want to repeat the same code with different types which can produce “Code duplication” ⛔

With Typescript Generics: “Don’t-Repeat-Yourself (DRY)” it helps us to write Generalized code

Problem Code-Example :

If for example, we are dealing with a case that we need to define the same function body for different arguments types! we will make code duplication!

interface User {name: string;email?: string;}interface Admin {admin: boolean;role?: string;name: string;}const user: User = {name: "Leonardo",};const admin: Admin = {name: "Leonardo",admin: true};function printUser(arg: User ): User{
console.log(arg);
return arg;
}function printAdmin(arg: Admin): Admin{
console.log(arg);
return arg;
}printUser(user);
printAdmin(admin)

Solution ☑️:

By using the Generics function provided by Typescript we can benefit and make our code reusable (example function that accepts the argument different type like is written in the example above )

interface User {name: string;email?: string;}interface Admin {admin: boolean;role?: string;name: string;}const user: User = {name: "Leonardo",};const admin: Admin = {name: "Leonardo",admin: true};function print<T>(arg: T): T{console.log(arg);return arg;}print<User>(user);
print<Admin>(admin)

#Tip4: Utility Types 🔧 🔨:

If you are working on a large project, you will find yourself with a lot of interfaces for data structures with duplication of properties!

Don’t repeat yourself

Readable, maintainable, and more consistent code

But with typescript our savior it offers us some utility functions to helps us reduce code and combine properties from each one interface

Omit

As the names suggest, it’s used to omit the properties of a type. Let’s look at an example.

type User = {id: string;name: string;email: string;};type userWithoutEmail = Omit<User, "email">;let user: userWithoutEmail = {id: " tets",name: "tete",email: "tete", // Error : Object literal may only specify known properties, and 'email' does not exist in type 'userWithoutEmail'.};

Pick: ✂️

: Pick is useful for maintaining the type checking when we only want a select number of properties from an existing interface

interface Person {name: string;age: number;friends: string[];}type UserNameAndAge = Pick<Person, "name" | "age">;let person: UserNameAndAge = {name: "hello",age: 23,};

Partial<Type> :

Partial is useful for creating a new type from the existing one by making all the properties optional :

interface Product {name: string;description: string;}let currentProduct: Product = {name: "FS2000",description: "Hippie Control",};function update(product: Product) {Object.assign(currentProduct, product);}function updateWithPartial(product: Partial<Product>) {Object.assign(currentProduct, product);}update({ name: "test" }); // Error :  Property 'description' is missing in type '{ name: string; }' but required in type 'Product'.updateWithPartial({ name: "test" });

Partial offers us the ability for making our function more reusable as you see in the example

Required<Type> 📒:

Utility that will set all the properties <Type> to required!

interface Props {
a?: number;
b?: string;
}
const obj: Props = { a: 5 };const obj2: Required<Props> = { a: 5 };
Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

Readonly <Type> 🔐:

This is a utility that will help to set all fields in object read-only and not reassigned!

interface Todo {name: string;}let item: Readonly<Todo> = { name: "test" };item.name = "test2";
//error TS2540: Cannot assign to 'name' because it is a read-only property.

And they're a lot of other utilities that we can't about in the next blogs and tutorials like ( Record<Keys,Type>,Exclude<Type, ExcludedUnion>,NonNullable<Type> etc ..)

#Tip5: Create classes/Interfaces from API response 🔧 🔨

This tip dedicated to Front-End Developers who are working with Typescript and are passing a long time defining data structures manually 🕠!

So there are a couple of options that

If you have a big, nested response from the API, it’s really tedious to type the corresponding interface(s) by hand. It’s a task that should be handled by machines! 🤖

There are a couple of options that help to get response API data structure generated like :

#Tip5: Path mapping to simplify verbose imports 🔧 🔨

If you’re dealing with a big project with several modules, it can become difficult to maintain the readability of your Typescript imports

Problem ⛔:

import { logger } from '../../../../util/logger';

Solution ☑️:

Typescript provides a solution “path mapping” by adding some paths alias into the tsconfig.json file in your project to resolve the long path :

{
"compilerOptions": {

"baseUrl": "src",
"paths": {
"actions/*": [ "app/actions/*" ],
"selectors/*": [ "app/selectors/*" ],
"ui/*": [ "app/ui/*" ],
"logger": [ "util/logger" ],
}
}
}

Now we can simply import the Logger class by :

import { logger } from '@Logger/utils';

#Tip5: Use Prettier for your code formatting.

Prettier is an opinionated code formatter with support for Typescript and so other many programming languages

You can follow this tutorial for vscode setup

References 📓 📕 :

Conclusion ❤️:

For Conclusion, I just tried to share with you good tips with Typescript that can be useful for writing a good code quality and any Javascript Developer can benefits from them with any framework that uses Typescript.

I hope it was clear and helpful! if you have any recommendations or issues feel free to ping me ✋.

Waiting for your feedback 😍

<script>alert('try your best')</script>