How To Use TypeScript to Avoid Bugs

TypeScript is a superset of JavaScript that is created to address common problems that JavaScript has.

A problem that JavaScript has is that all objects have dynamic types. This means that you have no way of knowing which properties an objects has without logging it in a debugger.

This creates a lot of frustration as you have to check every object yourself, slowing down development. Without static types, you also cannot have auto-complete in your editor as there is no way to know what are in those objects with 100% certainty.

Static typing is optional so you can still use objects as hashes.

Type Checks

Also, you can put any argument into your JavaScript functions, which brings the same difficulties as the dynamic types as there is no enforcement of what is passed in.

This creates problems as arguments that you’re supposed to pass in but didn’t will be undefined and then you get undefined errors. There is also nothing stopping you from passing in too many arguments.

With both checks, TypeScript makes code easier to understand and follow. You don’t have to worry about breaking things when you change code as the compiler will tell you that you got those basic errors.

In addition, dependency injection is part of TypeScript, which means you don’t have to resolve dependencies yourself. It also makes mocking dependencies easy for testing.

TypeScript provides features in the upcoming versions of JavaScript (which is not finalized yet) that might be handy for some developers.

TypeScript adds types to your objects by defining type files for your objects. It works by setting up a module and then including a module.d.ts file with it.

An example of this is in the TypeScript docs.

// Type definitions for [~THE LIBRARY NAME~] [~OPTIONAL VERSION NUMBER~]
// Project: [~THE PROJECT NAME~]
// Definitions by: [~YOUR NAME~] <[~A URL FOR YOU~]>
/*~ This is the module template file. You should rename it to index.d.ts
 *~ and place it in a folder with the same name as the module.
 *~ For example, if you were writing a file for "super-greeter", this
 *~ file should be 'super-greeter/index.d.ts'
 */
/*~ If this module is a UMD module that exposes a global variable 'myLib' when
 *~ loaded outside a module loader environment, declare that global here.
 *~ Otherwise, delete this declaration.
 */
export as namespace myLib;

/*~ If this module exports functions, declare them like so.
 */
export function myFunction(a: string): string;
export function myOtherFunction(a: number): number;

/*~ You can declare types that are available via importing the module */
export interface SomeType {
  name: string;
  length: number;
  extras?: string[];
}
/*~ You can declare properties of the module using const, let, or var */
export const myField: number;

According to the example from the website, you can define top-level properties as a line of code. And, nested properties can be defined by using the keyword namespace.

Combining Types

You do not have to use single types in TypeScript. Multiple types can be combined into one. Intersection types are data types where multiple data types are combined into one.

For example:

function(foo: Foo, bar: Bar): Foo & Bar {
    const result: Partial<Foo & Bar> = {};
    for (const prop in foo) {
        if (foo.hasOwnProperty(prop)) {
            (result as Foo)[prop] = foo[prop];
        }
    }
    for (const prop in bar) {
        if (bar.hasOwnProperty(prop)) {
            (result as Bar)[prop] = bar[prop];
        }
    }
    return result as Foo& Bar;
}

The object with the combined type gets to properties from both types.

Union types are a data type where an object can either be one type or another. For example:

let foo: number|string;

After that declaration, you can assign a number or a string to the variable and the TypeScript compiler will not throw an error.

TypeScript allows for static typing while still allowing for flexibility.

With TypeScript, ES6 or later, these features are all available. let and const should be used to avoid scope collision that happens with var.

You also get new types of tuples and enums. You can define a tuple, an array with a fixed number of elements and type, by doing:

let x: [string, number] = ["hello", 1];


If you set x to [1, 'hello'], the TypeScript compiler will fail.

You can define an enum type for a list of constants. For example:

enum Color {Red, Green, Blue}
let green: Color = Color.Green;

Arrays can have fixed types in TypeScript. For example, to define an array of numbers, we put:

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

Equivalently, you can assign an array like:

let x: Array<number> = [1, 2, 3];

If you set x to ['1','2','3'], compilation will fail.

TypeScript also adds an access modifier to properties and methods. You can optionally set properties and methods as public, private, or protected.

public properties and methods are available to all outside classes. protected properties and methods are available to subclasses and the current class. private properties and methods are available within the class only.

For example:

class classA {
  public publicProp: number;
  private privateProp: number;
}

class classB {
  public a: classA;
}

let b: classB = new classB();
b.a: classA = new classA();
b.a.publicProp = 1; // OK
b.a.privateProp = 1; // error

The last line will give an error since privateProp is not available to outside classes.

To include TypeScript in your project, you can run the following in your JavaScript project folder:

$ npm install typescript --save-dev

These are some basic and important advantages that TypeScript offers. With those features, it will save you a lot of time developing JavaScript apps!