What is it?

TypeScript, the language, is a strict superset of JavaScript ES6+ that adds a type system to JavaScript. It keeps all of the JavaScript syntax and features.

What TypeScript adds is the syntax to describe types of data, objects, classes, and functions.

What is a type?

According to wikipedia

A data type is a classification of data which tells the compiler or interpreter how the programmer intends to use the data.

Why should you care about types?

Type checking, the process of validating matching types in your programs, removes an entire category of bugs from your program by not allowing you to make them. Essentially, a type system is there to help you ensure correct code without even running your program.

JavaScript with Types


var a = 1;
var b = true;
var c = "test";
var d = null;
var e = undefined;
var f = [1,2,3];
var g = ["one","two"];
var h = new Date();
var i = Symbol("foo")

var a: number = 1;
var b: boolean = true;
var c: string = "test";
var d: null = null;
var e: undefined = undefined;
var f: number[] = [1,2,3]
var g: string[] = ["one","two"]
var h: Date = new Date();
var i: symbol = Symbol("foo")

What happens when we get things wrong

var a: number = "test";
var b: boolean = 3;
var c: string = false;
example.ts|1 col 5 error| Type 'string' is not assignable to type 'number'.
example.ts|2 col 5 error| Type 'number' is not assignable to type 'boolean'.
example.ts|3 col 5 error| Type 'boolean' is not assignable to type 'string'.

Any Type

The TypeScript type system supports gradual typing, in other words, it will let you add types as need. The "any" type allows you to put anything inside. It is an escape hatch from the type system. TypeScript also has syntax for type casting if needed. Ideally, they should be used sparingly.

var a: any = 1;
var b: any = true;
var c: any = "test";
var d: any = null;
var e: any = undefined;
var f: any[] = [1,2,3]
var g: any = ["one","two"]


function sum(x, y) {
    return x + y;

function types

function sum(x: number, y: number): number {
    return x + y;


var point1 = {
    x: 1,
    y: 4,
    name: "start"

object types

var point1: {x: number, y: number, z?: number, name: string} = {
    x: 1,
    y: 4,
    name: "start"

The ? after property names indicates the property is optional

Type Aliases

type Point = {x: number, y: number, z?: number, name: string};
var point1: Point = {
    x: 1,
    y: 4,
    name: "start"
var point2: Point = {
    x: 4,
    y: 10,
    z: -1,
    name: "end"

String Literal Types

You can treat string constants as types.

type PrimaryColor = "red" | "blue" | "green";
function adjustColor(color: PrimaryColor, value: number) { /* */ }
adjustColor("red", 123) //ok
adjustColor("purple", 255) //Error


You can create named numeric contants with enums

enum Directions {

function Go(d: Directions): string {
    return "You went " + Directions[d] + " aka " + d;

Go(Directions.Left); //You went left aka 3

Numeric Literal Types

type EightBit = 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7;
let coolbit: EightBit = 7; //ok
let badbit: EightBit = 8; //error

Type Aliases

Type aliases can be used to alias primitives, which can be useful for documentation

type Dollars = number;
type Name = string;
function BankBalance(person: Name): Dollars {
    return 3.50

Function Type Alias

Functions can also be described with type aliases

function sum(x: number, y: number): number {
    return x + y;

type binary_op = (x: number, y: number) => number;
var sum2: (x: number, y: number) => number = sum;
var sum3: binary_op = sum;


Typescript supports the es6 syntax for classes

class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
class SoftwareEngineer extends Person {
    prog_language: string;
    constructor(name: string, prog_language: string) {
var joeTester: Person = new SoftwareEngineer("Joe", "TypeScript");

Class syntax

Typescript simplifies the "this.property = property" pattern with the public keyword. It also supports private and protected attributes on classes, but these are compiler enforced only.

class Person {
    constructor(public name: string) {}
class SoftwareEngineer extends Person {
    private prog_language: string;
    constructor(public name: string, private prog_language: string) {
        this.prog_language = prog_language;

Abstract Classes

Abstract classes are base classed which are meant to be inherited from. They cannot be instantiated directly. They specify methods which must be implemented in the subclasses. They are allowed to include implementations of methods to share as well.

abstract class Animal {
    abstract makeSound(): void;
    move(): void {
        console.log("roaming the earth...");
class Mammal extends Animal {
    makeSound(): void {
        console.log("A noise!");


Interfaces can be used to describe objects, classes, and functions. They are similar to abstract classes however, they cannot contain an implementation of a function. Interfaces fill a similar role to type aliases, but they can do and describe a few more things.

//object type
interface Point {
    x: number;
    y: number;
    z?: number;
    label: string;
var origin: Point = {x: 0, y: 0, z:0, label: "origin"};
//function type
interface binary_op {
    (x: number, y: number): number
var add_one: binary_op = function(x: number, y: number): number {
    return x + y + 1

Classes implementing interfaces

Interfaces can ensure that a class must implement specific methods and attributes.

interface ClockInterface {
    currentTime: Date;
    setTime(d: Date);
class Clock implements ClockInterface {
    currentTime: Date;
    setTime(d: Date) {
        this.currentTime = d;
    constructor(hour: number, minute: number) {}

Indexable types

Interfaces can also be used to describe container objects like arrays and objects

interface PhoneBook {
    [phoneNumber: string]: Person
class Person {
    constructor(public name: string) {}
var yellowpages: PhoneBook = {
    "1112223333": new Person("John"),
    "3334445555": new Person("Jane")

interface GuestList {
    [index: number]: Person
var partylist: GuestList = [new Person("John"), new Person("Jane")]

Immutable data

Variables, classes, and interfaces can enforce immutability on variables and properties. TypeScript also comes with a ReadonlyArray< T > type. Only the compiler forbids reassignment.

const example = true;
interface ImmutablePoint {
    readonly x: number;
    readonly y: number;
class Pet {
    constructor(public readonly name: string) {};
let hex_letters: ReadonlyArray< string > = ['A','B','C','D','E','F'];

Tuple Types

Tuples are a way to describe fixed length arrays with types for each index.

let person: [string, number] = ["John Smith", 30];
person = ["Jane Doe", 30]; //ok
person = [20, "Joe Tester"] // Error

Union Types

We can use union types to describe pieces of data that can be two or more types of data. The | operator lets us create a union of types.

var password: string | number = "test"
password = 1234 //also ok!

function validatePassword(password: number | string): boolean {
    if(typeof password == "number") {
        return password > 1000;
        return password.length > 4;

Type Guards

Once you have the ability to mash types together with Union Types, TypeScript also gives us the ability to pull types apart with type guards.

class BinaryPassword {
  data: ArrayBuffer;
  constructor(public data: ArrayBuffer) {}
type EncryptedPassword = {password: string};
function IsEncryptedPassword(obj: Any): obj is Password {
    return obj.password !== undefined

type Password = BinaryPassword | EncryptedPassword | number | string;
function validatePassword(password: Password): boolean {
    if(password instanceof BinaryPassword) {
        return true;
    }else if(IsEncryptedPassword(password)) {
        return true;
    }else if(typeof password == "number") {
        return number > 1000;
    }else if(typeof password == "string" ){
        return string.length > 4;
        throw new TypeError("Not a valid password type");

Intersection Types

These allow you describe an object which is one type AND another type. A common JavaScript pattern called the "mixin" pattern can be described with intersection types.

type Fahrenheit = {fahrenheit: number};
type Celcius = {celcius?: number};
function convertToCelcius(temp: Fahrenheit): Fahrenheit & Celcius {
    let result: Fahrenheit & Celcius = temp;
    let c_temp = (temp.fahrenheit-32)*(5/9);
    result.celcius = c_temp;
    return result;

Structural Typing

Most languages traditional languages use a nominal type system, which means that when they compare types, they look at the name of the type to determine if an expression is well typed.

Typescript has a structural type system which means that it compares the shape of types rather than just the name.

Structural Typing example

type Person = {name: string, age: number};
type Pet    = {name: string, age: number, breed: string};

function HappyBirthday(p: Person) {
    console.log(`Happy Birthday ${p.name}!`+
        `You are now ${p.age} years old!`);
let myDoge: Pet = {name: "Spot", age: 4, breed: "pug"};
//Happy Birthday Spot! You are now 5 years old!


Typescript has a config file you can generate for your project called "tsconfig.json". Among other things, this file allows you to ramp up or ramp down strictness in typing. Here is an example of a maximally strict config file.

    "compilerOptions": {
        "module": "commonjs",
        "sourceMap": true,
        "target": "ES6",
        "noImplicitAny": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "noFallthroughCasesInSwitch": true,
        "strictNullChecks": true

Control Flow Analysis

Typescript can also prevent common errors because it understands how data and types flow through JavaScript.

var test: number;
console.log(test); //error unassigned variable

function goTime(isGoTime: boolean) {
    if(isGoTime) {
        return isGoTime;
        return !isGoTime;
    return "asdf"; //error unreachable code

switch(test) {
    case 1:
        console.log("Hello world!"); //error switch case fall-through
    case 2:
        console.log("Goodnight world!");

Discriminated Unions

TypeScript has the ability to create complex types called a discriminated unions. Combining literal types or type guard functions TypeScript can narrow a union type into a specific type to define per type behavior.

interface Square {
    kind: "square";
    size: number;
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
interface Circle {
    kind: "circle";
    radius: number;
type Shape = Square | Rectangle | Circle;
function area(s: Shape) {
    switch (s.kind) {
        case "square": return s.size * s.size;
        case "rectangle": return s.height * s.width;
        case "circle": return Math.PI * s.radius ** 2;

Generic (aka Parametric) Types

These functions take a type as a parameter. A is a variable. Interfaces, classes, and type aliases all support generics

function head< A >(xs: A[]): A { return xs[0]; }

function tail< A >(xs: A[]): A[] { return xs.slice(1); }
let one = head< number >([1,2,3]);
let letter = head< string >(["a","b"]);
type Container< A > = { value: A };

class LinkedList< A > {
  data: A;
  next: LinkedList< A >;
  constructor(public value: A, public next: LinkedList< A >) { }

Generic Type Constraints

It is possible to add constraints to generic types.

interface Lengthwise {
  length: number;
function logLength< T extends Lengthwise >(arg: T): T {
    return arg;

Lookup Types

A common pattern in JavaScript is to use indirection when working with objects of a certain kind .

inteface Person {
    name: string;
    age: number;
let person = {name: "Joe Tester", age: 30};
let personProp: keyof Person = "name"; //ok
let personProp2: keyof Person = "test"; //error
function getProperty< T, K extends keyof T >(p: T, prop: keyof K): T[K] {
    return p[prop];
let age: number = getProperty(person, "age");

Additional Topics

  • void, and never types
  • Function type aliases, subtypes, and variance
  • Mapped types
  • Modules
  • static class methods
  • JSX

The Goal of a Type System

The goal of a type system is to ensure that our program is free of bugs. However, to actually get this benefit takes a bit of design. The trick is to push business logic into the types. The goal is to make invalid states inexpressible. If you have an expressive type system, it becomes easier to describe your business logic as types and thereby create code that is provably correct.


sudo npm install -g typescript

installs the command tsc

How do you use it?

tsc filename.ts

Upon running tsc on a ts file it will generate a .js file of the same name, which is the translation of the typescript code