Categories
TypeScript Best Practices

TypeScript Best Practices — Literal Types and Promises

Spread the love

To make code easy to read and maintain, we should follow some best practices.

In this article, we’ll look at some best practices we should follow to make everyone’s lives easier.

No delete Expressions with Computed Key Expressions

Using delete to remove computed key expressions is a bad idea.

It’s not secure and it can’t be optimized.

So instead of writing:

delete foo[bar];

We can just leave the object as is.

No Empty Blocks

We shouldn’t have empty blocks in our code.

They’re useless.

For instance, we should remove code like:

function foo(){}

No Floating Promises

Floating promises re promises that aren’t storing or returning any data.

Therefore, it’s not actually doing anything.

For instance, if we have:

Promise.resolve(1);

Then we should do something with it.

Unhandled promises can lead to unexpected behavior since they give us a value in an indeterminate amount of time.

Don’t Use for-in Loops with Arrays

We shouldn’t use for-in loops with arrays. It’s meant to be used with regular objects.

The order of iteration isn’t guaranteed.

So instead of using for-in like:

for (const i in array){
  console.log(array[i]);
}

We can write:

for (const a of array){
  console.log(a);
}

We replaced in with of and a for the array item.

Now we can loop through them easily in order.

No Inferred Empty Object Type

We shouldn’t have empty object types since we should have a more specific type for our variables, parameters, and return types.

Instead of writing:

const obj: {} = {};

We write:

const obj: { foo: number } = { foo: 2 };

No Invalid this

We shouldn’t use this outside of classes or object literals.

It just causes confusion since this is supposed to be a class instance.

For instance, we can write:

const obj = {
  a: 1,
  b() {
    console.log(this.a)
  }
}

or:

class Foo {
  constructor() {
    this.a = 1;
  }

  bar() {
    console.log(this.a)
  }
}

No null Keyword

We should just use undefined for all empty values instead of mixing null or undefined .

They serve the same purpose so we don’t need both.

undefined also has type undefined so it’s easier to check it.

Therefore, just use undefined everywhere in our code.

No null and undefined Union Types

Creating a type with null and undefined is redundant.

This is because our variable, parameter or returned value can just have null or undefined or any other we specified.

We should make the type more restrictive so that we can actually take advantage of the type checking capability of TypeScript.

No Object Literal Type Assertion

Having type assertion with object literal types will hide errors with excess properties.

Even if the object is missing some required fields, the data type assertion will override whatever type it has without the assertion.

Therefore, we should deal with it without forcing the type to be an object literal structure.

So we shouldn’t write:

const foo = {} as { bar: number };

We write:

const foo: Foo = {};

where Foo is an interface, type or type alias.

No Promise as Boolean

We shouldn’t use an await expressions as a boolean expression.

await only has a promise for giving the actual value in the future.

It’s not the actual value we want to check for.

Therefore, we should assign the await ed value into a new variable so that we can do the check with the actual value.

For instance, instead of writing:

async function bar(personPromise: Promise<Person> ) {
  if (await personPromise) {
    console.log("person retrieved")
  }
}

We write:

async function bar(personPromise: Promise<Person> ) {
  const person = await personPromise;
  if (person) {
    console.log("person retrieved");
  }
}

No Return Await

We shouldn’t use return and await on the same line.

It just adds extra time before the promise is resolved but the promise is returned no matter if there’s an await or not.

For instance, instead of writing:

async function bar() {
  return await aPromise;
}

We write:

async function bar() {
  return aPromise;
}

Conclusion

delete operator shouldn’t be used with the computed expression.

for-in loops shouldn’t be used with arrays.

this should only be used for object literals and classes.

Literal type assertions shouldn’t be used to force the structure of the object.

await should be used in certain places only in async functions.

By John Au-Yeung

Web developer specializing in React, Vue, and front end development.

Leave a Reply

Your email address will not be published. Required fields are marked *