Categories
TypeScript Best Practices

TypeScript Best Practices — Type Assertions and Type Annotations

Spread the love

TypeScript is an easy to learn extension of JavaScript. It’s easy to write programs that run and does something. However, it’s hard to account for all the uses cases and write robust TypeScript code.

In this article, we’ll look at the best practices to following when writing code with TypeScript, including type assertions and having explicit type annotations.

Enforce Consistent Usage of Type Assertions

Type assertion styles should be consistent across our project.

Type assertions are also referred to as data type casting in TypeScript.

However, they’re technically different.

We can either use the as keyword or <> to add type assertions to our values.

For instance, we can either stick with:

let x = "foo" as string;

or:

let x = <string>"foo";

const is also allowed with the rule. It’s available since TypeScript 3.4 and it allows us to make something read-only.

For instance, we can write:

let x = "foo" as const;

or:

let x = <const>"foo";

Consistent Type Definitions with interface or type

In TypeScript, we can define interfaces for annotating types.

Also, we can create type alias with the type keyword to do the same thing.

It’s a good idea to be consistent with what we use when we define types.

For instance, we can either stick with interfaces:

interface Foo {
  a: string;
  b: number;
}

or type aliases:

type Foo = {
  a: string;
  b: number;
};

Require Explicit Return Types on Functions and Class Methods

It’s a good idea to add return type annotations for functions and class methods.

This way, we know what each function or method returns.

For instance, instead of writing:

function foo() {
  return 'bar';
}

We can write:

function foo: string() {
  return 'bar';
}

Likewise, with class methods, instead of writing:

class Foo{
  method() {
    return 'bar';
  }
}

We write:

class Foo{
  method(): string {
    return 'bar';
  }
}

If our method returns nothing, we use the void return type:

function foo(): void {
  return;
}

or:

class Foo{
  method(): void {
    return;
  }
}

Require Explicit Accessibility Modifiers on Class Properties and Methods

If we leave out the accessibility modifiers in classes, then it may be hard for people to understand whether a class property or method is accessible or not.

By default, if we leave them out, the class member is public.

So we may also want to restrict access to some members in most cases.

Therefore, we should include them.

So instead of writing:

class Foo {
  foo() {
    console.log("foo");
  }

  bar() {
    //...
  }
}

We may want to restrict access to some members by writing:

class Foo {
  foo() {
    console.log("foo");
  }

  private bar() {
    //...
  }
}

This way, the TypeScript compiler will give us an error if we try to compile it.

Access modifiers include public , private , readonly and protected .

A public member is available to everything.

A private member is only available within the class.

A readonly member is public but it’s read-only.

A protected member is only available within the class or any child class.

Require Explicit Return and Argument Types on Exported Functions and Classes’ Public Class Methods

To make working with functions and public methods of classes easier, we should add explicit argument and return types so that we can get autocomplete and errors when we work with them.

For instance, instead of writing:

export function test() {
  return;
}

We write:

export function test(): void {
  return;
}

And instead of writing:

class Foo {
  method() {
    return;
  }
}

We write:

export class Foo {
  method(): void {
    return;
  }
}

Likewise, with argument types, we write:

export class Foo {
  method(foo: string): string {
    return foo;
  }
}

and:

export function test(foo: string): string{
  return foo;
}

Now we don’t have to guess or look up the types of exported classes and functions.

Conclusion

We should annotate return and argument types so don’t have to look up the types of functions and class methods that are exported.

Consistent usage of type assertions notation may also be a good idea.

Also, it may be a good idea to have consistent usage of interface or type to create new types.

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 *