TypeScript Best Practices — Member Access, Loops, and Function Types
By: John Au-Yeung
Tags: JavaScript, Software Development, Web Development, Programming, Technology

Photo by Whitney Wright on Unsplash

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 disallowing member access on any typed variables.

Also, we look at why we want to use const assertions.

We also look at why import should be used instead of require for importing modules.

We find out how we can use interfaces to specify the type of functions.

And we look at why the for-of loop is better than the regular for loop.

No Member Access on any Typed Variables

any may leak into our codebase in nested entities.

For instance, we may have type declarations that have the any type.

Therefore, if we allow those nested members to be accessed, we may run into type errors at runtime.

So instead of writing:

declare const anyObj: { prop: any };

anyObj.prop.a.b;

We should write:

declare const anyObj: { prop: string };

anyObj.prop;

Now that we know anyObj.prop must be a string, we can access it safely.

Don’t Return any From a Function

We shouldn't return anything with the any type in a function.

Instead, we should add a return type annotation so that we know what it’s returning.

For instance, instead of writing:

function foo() {

return 1 as any;

}

or:

function arr() {

return [] as any[];

}

We should write:

function arr(): number[] {

return [1, 2];

}

or:

function foo(): number {

return 1;

}

Now we know what the functions return.

No Unused Variables and Arguments

Unused variables and arguments are useless, so we probably shouldn’t have them in our code.

We can remove variable declarations like var , const or let variables that aren’t used.

Likewise, functions and classes that aren’t used can be removed.

enums, interface, and type declarations that aren't used can also be removed.

Class members like methods, instance variables, parameters can all be removed if they aren’t used.

This also applies to import statements.

No requires Statements Except in Import Statements

We shouldn’t use require statements any more since ES6 modules have become standard.

Therefore, instead of writing:

const foo = require('foo');

We write:

import foo = require('foo');

or:

import foo from 'foo';

Use as const Over Litreral Types

const assertions are better than literal types since they make values constant.

If we use const assertions, literal types can’t be widened, and object and array entries become readonly .

For instance, instead of writing:

let bar: 2 = 2;

or:

let bar = { bar: 'baz' as 'baz' };

We can write:

let foo = 'bar' as const;

or:

let foo = { bar: 'baz' };

Use for-of Loop Instead of a for Loop

for-of loops let us loop through items with a simple loop instead of having to set up looping conditions and index variables.

It also works with any kind of iterable object instead of objects with index and the length property.

For instance, instead of writing:

for (let i = 0; i < arr.length; i++) {

console.log(arr[i]);

}

We write:

for (const x of arr) {

console.log(x);

}

As we can see, the for-of loop is much simpler if we just want to loop through all items in an iterable object.

Photo by Brooke Lark on Unsplash

Use Function Types Instead of Interfaces with Call Signatures

We can use function types instead of interfaces or object type literals with a single call signature.

For instance, instead of writing:

function foo(bar: { (): number }): number {

return bar();

}

We write:

interface Foo {

(): void;

bar: number;

} const foo: Foo = () => {};

foo.bar = 1;

foo();

We have a function that returns nothing and has a bar property that’s a number.

Conclusion

We may want to use as const over literal types to prevent the type of the variable from widening and to make them, read-only.

Also, we want to use import instead of require for importing modules.

We also want to use interfaces instead of functions to specify the type of functions.

Finally, the for-of loop is better than the for loop in most cases.