An Introduction to TypeScript

I attended GR DevDay on March 12.  One of the things on my to-do list lately has been to learn TypeScript, and there just happened to be a session on TypeScript. As part of my learning process, I decided to post my notes from the session.

What is TypeScript?

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Since it is a superset of JavaScript, valid JavaScript code is also valid TypeScript code. There are also syntactical elements from Java and C#.

TypeScript Data Types

  • Boolean
  • Number
  • String
  • Array
  • Any
  • Enum
  • Void

The default data type is “any," which is equivalent to a JavaScript “var” and similar to “object” in C#.

Define a Variable

var n: number;
n = 1;

Define a Variable with a Default Value

var s: string = "mystring";

The compiler can infer the type based on the default value, so the “:string” part is unnecessary:

var s = "mystring";

Define a Typed Array

var a: number[];
a = [1, 2, 3];

If you now try to add an element that is not a number, you’ll get an error:

 Code1

Define a Return Type for a Function

Notice that the syntax is very similar to defining a data type on a variable. Since functions are first-class objects in JavaScript, it makes sense that the syntax would be similar here:

function f(): void {
     // return type is void 
 }

Enums

Enumerations are particularly cool in the way that such simple code gets transpiled into JavaScript. First, the TypeScript syntax for an enum definition:

enum CardSuit {
     Spades = 0,
     Clubs,
     Diamonds,
     Hearts
 }

If we peak under the hood, here’s the not-so-simple JavaScript generated for that simple enumeration:

var CardSuit;
(function (CardSuit) {
    CardSuit[CardSuit["Spades"] = 0] = "Spades";     
    CardSuit[CardSuit["Clubs"] = 1] = "Clubs"; 
    CardSuit[CardSuit["Diamonds"] = 2] = "Diamonds";
    CardSuit[CardSuit["Hearts"] = 3] = "Hearts"; 
})(CardSuit || (CardSuit = {}));

This all looks pretty crazy, but it lets you do some really cool things. First, as you would expect, you can reference any of these enumerated values:

var card = CardSuit.Spades;

But once you have such a variable defined, you can get the string representation of that enumerated value:

var cardName = CardSuit[card];

This will result in a string "Spades" stored in the variable cardName.

Interfaces

interface IPoint2D {
     x: number,
     y: number
}

Classes

This is where the Java influences become pretty obvious. One exception, however, is that rather than the constructor being a method whose name matches the class name (like it is in both Java and C#), the constructor is instead a method called “constructor." The Java syntax for implementing an interface (namely, the “implements” keyword) is used here:

class Point2D implements IPoint2D {
     public x: number;
     public y: number;
  
     constructor(x: number, y: number) {
         this.x = x;
         this.y = y;
     }
 }

Class Inheritance

Just as TypeScript uses the Java syntax for implementing an interface, it also uses Java syntax for class inheritance – the “extends” keyword. The keyword “super” is also used to refer to the superclass. In C#, we think of it as a base class:

class Point3D extends Point2D {
     public z: number;

     constructor(x: number, y: number, z: number) {
         super(x, y);
         this.z = z;
     }
 }

Function Overloads

This is a little crazy (and fun, too… don’t worry). Each function overload must be declared without a body. After all overloads have been defined, an extra function is then defined with a body such that each parameter matches the corresponding parameter from all overloads. At this point, an example will go a long way to help us understand.

I’m going to first create a Person class. Really simple; just a name and an age:

class Person {
    public name: string;
    public age: number;
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }
}

Next, I’m going to create a PersonList class. This will just be a wrapper around a typed array of Person objects. I’m going to want two versions of an “add” method:  one will let me add an already-created Person instance, and the other will let me pass in the name and age:

class PersonList {
    public items: Person[] = [];
    public add(person: Person): void
    public add(name: string, age: number): void
    public add(personOrName: Person | string, age?: number): void {
        if (personOrName instanceof Person) {
            this.items.push(personOrName);
            return;
        }
        if (personOrName instanceof String) {
            var person = new Person(personOrName, age);
            this.items.push(person);
            return;
        }
    }
}

The actual implementation of the “add” method uses a union type (Person|string) to indicate that it can be either of those two types. The second parameter only exists in one of the overloads, so it uses the question mark to indicate it is optional. Inside the body of this method, we are responsible for checking the type of the parameters. We use the “instanceof” keyword for that. Notice that when type checking with instanceof, we have to use String with a capital "S."

This is just scratching the surface of TypeScript. There was a lot more covered in the GR DevDay session I went to, and there’s even more in the language specification that we didn’t have time to cover in that session. Hopefully, this gives you enough to get started and piques your interest enough to go out and learn more on your own. I know it has for me.

A great site to use as you learn TypeScript is the TypeScript Playground. This will give you side-by-side TypeScript and Javascript, so you can see what your TypeScript code converts to in JavaScript.

Finally, I want to give a huge shout-out to Jody Gustafson – the presenter of the TypeScript session that inspired this blog post.  Well done!