TypeScript basics

2018-02-03

TypeScript is an open source programming language created and maintained by Microsoft. It is based on JavaScript and it extends the JavaScript language with static typing, classes, generics, etc.

TypeScript’s syntax is a strict superset of JavaScript, meaning that any valid JavaScript syntax is in principle also valid TypeScript syntax. Note, however, that this does not mean that TypeScript’s compiler will be happy with every piece of valid JavaScript. As an example, see the section on type inference below. TypeScript puts some restrictions on the way your code deals with types, with the goal of preventing mistakes and unexpected behavior.

Instead of requiring a separate interpreter, Typescript compiles into plain JavaScript which can be executed by browsers, Node.js, or other JavaScript engines. Note that syntactically valid TypeScript code with compiler errors can still be compiled to JavaScript. The Typescript compiler is only warning us that we will likely see some unexpected behavior.

Most important features

Type annotations

Let’s start from a simple piece of JavaScript where a function greet takes a string person and shows a message greeting the person.

function greet(person) {
    alert('Hello, ' + person);
}

let user = 'user';

greet(user); // shows message saying "Hello, user"

This greet function works when we pass it a string, but nothing prevents us from making a mistake and passing something else, yielding unexpected results.

let user = { name: 'visitor' };

greet(user); // shows message saying "Hello, [object Object]"

Using TypeScript’s type annotations, we can actually enforce that the person parameter should be a string.

function greet(person: string) {
    alert('Hello, ' + person);
}

let user = { name: 'visitor' };

greet(user); // typescript compiler error (type mismatch)

Type inference

When you declare a variable, TypeScript will infer the variable’s type and perform type checking based on that.

let user = 'test';

// some other code

user = { name: 'test' }; // typescript compiler error

Here, TypeScript infers that the type of the user variable is string. It then prevents you from assigning values of another type to that variable, as that is typically a source of errors and is generally considered bad practice. JavaScript is not able to enforce these kinds of restrictions.

You can also explicitly specify the type of a variable.

let user: string = 'test';

Interfaces

Interfaces allow you to describe what an object should look like. In the following example, we are defining Person as an object that has a firstName and lastName property (both strings).

interface Person {
    firstName: string;
    lastName: string;
}

function greetPerson(person: Person) {
    alert('Hello, ' + person.firstName + ' ' + person.lastName);
}

let user = { firstName: 'Test', lastName: 'User' };

greetPerson(user);

We don’t need to make it explicit that user is a Person when we declare it. When we pass user to the greet function, TypeScript will automatically check if it has the right structure.

Classes

TypeScript supports class-based object-oriented programming.

Let’s look at an example of what a class definition looks like.

class Student {
    public firstName: string;
    public lastName: string;
    public fullName: string;

    constructor(firstName: string, lastName: string) {
        this.firstName = firstName;
        this.lastName = lastName;

        this.fullName = firstName + ' ' + lastName;
    }
}

let user = new Student('Jane', 'User');

The Student class defines some name properties and a constructor for creating new instances.

If you don’t like the repetition in the properties and constructor, TypeScript also allows you to explicitly declare properties in the constructor parameters. They are then called parameter properties. The following code is equivalent to the code above:

class Student {
    public fullName: string;

    constructor(public firstName: string,
            public lastName: string) {
        this.fullName = firstName + ' ' + lastName;
    }
}

let user = new Student('Jane', 'User');

Note that classes and interfaces play well together. Because a Student has a firstName and lastName, we can pass it to a function expecting a Person (like for example our greetPerson function).

Access modifiers and readonly

Class members (properties, constructors and methods) are public by default but can also be marked private (cannot be accessed from outside the class) and protected (like private, but can also be accessed from deriving classes).

Properties can also be made readonly, meaning that they cannot be changed and they must be initialized at their declaration or from the constructor.

Classes implementing interfaces

Classes can implement interfaces. This way, an interface can enforce that a class meets a particular contract.

interface Person {
    firstName: string;
    lastName: string;
}

class Student implements Person {
    public fullName: string;

    constructor(public firstName: string,
            public lastName: string) {
        this.fullName = firstName + ' ' + lastName;
    }
}

Interfaces can also describe methods that a class should implement. Note that interfaces only describe the public contract of a class, you cannot use them to constrain the internal implementation of a class.

Inheritance

TypeScript provides inheritance for interfaces and classes using the extends keyword.

The following code provides an example of interface inheritance.

interface Person {
    firstName: string;
    lastName: string;
}

interface Employee extends Person {
    company: string;
}

let employee: Employee = { company: 'ACME'} // compiler error

The code yields a compiler error because the object literal we are passing has no firstName and lastName.

Class inheritance lets you inherit and override methods.

class Animal {
    makeNoise() {
        console.log(`Hello there!`);
    }
}

class Dog extends Animal {
    makeNoise() {
        console.log('Woof! Woof!');
        super.makeNoise();
    }
}

let dog = new Dog();

dog.makeNoise();
// Woof! Woof!
// Hello there!

Generics

TypeScript provides generics that you can use to create reusable components.

class Wrapper<T> {
    constructor(public wrapped: T) {}

    public replaceWrapped(newWrapped: T) {
        this.wrapped = newWrapped;
    }
}

let wrapper = new Wrapper('test');
wrapper.replaceWrapped(1); // error: type mismatch

Getting started

The TypeScript quickstart page contains some resources to get you started, including frameworks like Angular that use TypeScript.

If you are just looking for a quick way to play around with the language, you can use the Playground.

I only gave a very basic overview of the most important features here. For more details regarding the language, you can take a look at the Handbook.