Typescript

What is it for?

  • it adds types to Javascript
  • statically analysable code
  • IDE autocompletion support
  • easy refactoring
  • type correctness

What's different?

  • .ts file extension instead of .js
  • compiled
  • needs type definitions
    for using external libraries

Install typescript

sudo npm install -g typescript

Initialize typescript 

tsc --init

tsconfig.json

  • configuration for typescript compiler
  • for node use "target"="es6"

Typescript hello world

  • create index.ts
  • console.log('Hello world')
  • run tsc
  • run node index.js

Where are types?

Basic types

  • boolean
  • number
  • string
  • array
  • any
  • unknown
  • null, undefined
  • void, never

How to use them?

let myNumericVariable: number = 5;

let myString: string = 'hello';

let myArray: string[] = ['hello', 'hi'];
let myArray: Array<string> = ['hello', 'hi'];

And in functions...

function convertToUppercase(input: string): string {
    return input.toUpperCase();
}

function createPerson(
    id: number,
    firstName: string,
    lastName: string
  ): {id: number, name: string} {
    return {
        id,
        name: `${firstName} ${lastName}`,
    };
}

Let's make a calculator

  • initialize and configure typescript
  • implement functions for:
    • addition
    • subtraction
    • sum of numbers in array

Modules

Modules in typescript

  • when using multiple files
  • export
  • import

Named export

import { calculateSum } from './my-file';

let result: number = calculateSum([1,2,3])

Import

export function calculateSum(numbers: number[]): number {
    return numbers.reduce((a, b) => a + b)
}

Export default

import renamedSum from './my-file';

let result: number = renamedSum([1,2,3]);

Import

export default function calculateSum(numbers: number[]): number {
    return numbers.reduce((a, b) => a + b)
}
  • only one default export per module

Try it out

  • move calculation functions to separate file (calculations.ts)
  • use import/export
  • create const for PI = 3.14 to a pi.ts and export it as default export

Importing libraries

import lodash from 'lodash';
lodash.without([1,2,3], 2);
  • remember to install the library first!

You will also need type definitions for lodash

What is type definition?

Let's use lodash in Typescript

  • create array of people with id and name
  • use lodash.find to find person by id
  • initialize and configure typescript
  • npm install @types/lodash --save-dev

 

const people = [
    {
        id: 1,
        name: 'Peter'
    },
    {
        id: 2,
        name: 'Rachel'
    },
    {
        id: 3,
        name: 'John'
    },
    {
        id: 4,
        name: 'Tim'
    },
    {
        id: 5,
        name: 'Zed'
    }
];

How about person object?

const people = [
    {
        id: 1,
        name: 'Peter'
    },
    {
        id: 2,
        name: 'Rachel'
    },
    {
        id: 3,
        name: 'John'
    },
    {
        id: 4,
        name: 'Tim'
    },
    {
        id: 5,
        name: 'Zed'
    }
];

Let's define Person

interface Person {
    id: number;
    name: string;
}

Interface

  • describes the shape of object
  • no implementation
  • not included in transpiled code
interface Person {
    id: number;
    name: string;
    age?: number;
    shoutOnSomebody(name: string): string;
}
function createPerson(id: number, firstName: string, lastName: string)
    : {id: number, name: string} {
    return {
        id,
        name: `${firstName} ${lastName}`,
    };
}

// becomes this:

interface Person {
    id: number;
    name: string;
}

function createPerson(id: number, firstName: string, lastName: string): Person {
    return {
        id,
        name: `${firstName} ${lastName}`,
    };
}

Use Person as return type

Known type under any property

interface Person {
    name: string;
    age: number;
}

interface RandomKeys {
    [key: string]: Person
}

let item: RandomKeys = {
    martin: {
        name: 'Martin',
        age: 32
    },
    homer: {
        name: 'Homer',
        age: 50
    }
}

console.log(item.martin.name)
console.log(item['martin'].name)

Describe a function

interface PrintFunction {
    (text: string): void;
}

let printer: PrintFunction;
printer = (text) => console.log(text)
printer = (text) => console.error(text)

Interfaces excercise

  • define interface for a calculator
    • add(a, b) → number
    • subtract(a, b) → number
    • PI = 3.14
  • create an object which fits such interface
let calculator: Calculator = {...}

Classes

  • same as ES6 classes
  • encapsulate behaviour
class Animal {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    sayName() {
        console.log(`My name is ${this.name}`);
    }
}

Constructor shortcut

  • defines property right in the constructor
class Animal {
    constructor(public name: string) {
    }

    sayName() {
        console.log(`My name is ${this.name}`);
    }
}

Usage

let dog = new Animal('Lassie');
let mouse = new Animal('Mickey');

dog.sayName();
mouse.sayName();
  • we create instance of class

Class inheritance

  • class may inherit properties and methods from another class
  • parent class should be always more general
class Dog extends Animal {

    constructor(name: string) {
        super(name);
    }

    bark() {
        console.log(`Bark!`);
    }
}

public / private / protected

  • for both properties and methods 
  • limits access from outside
  • public
    • is default
    • everything may access the property
  • private
    • accessible only from the class
  • protected
    • accessible only from class and its derived classes

Let's describe an army

  • soldier with sword and shield
  • archer with a bow
  • knight on a horse with sword
  • everyone has some coordinates [X, Y]
  • abilities
    • use weapons
    • move

Union type

let value: number | string = 5; // ok
value = 'hi'; // also ok

let pet: Dog | Turtle | Cat;

Types

Type

  • you can define your own types
  • similar to interface
  • interface describes object-like stuff
  • type can use also primitive types
type Person = {
  name: string;
  age: number;
}

type Answer = 'yes' | 'no';
let x: Answer;

type AnswerList = {
  [key in Answer]: boolean;
}
const list: AnswerList = {
  yes: true,
  no: false
}

Type

  • work with primitive types
  • more flexible (union etc)
  • sometimes don't appear in error messages

Interface

  • redefinition extends
  • for object types
  • always appear with original name in error messages

Type guards

  • tells the compiler true type
type Square = {
  type: 'square';
  size: number;
};

type Circle = {
  type: 'circle';
  radius: number;
};

type Shape = Square | Circle;

const shape: Shape = ...
if (isSquare(shape)) {
  console.log(`Size is ${shape.size}`);
}

function isSquare(shape: Shape): shape is Square {
  return shape.type === 'square';
}

typeof

  • get type from data 
const obj = {
  id: 1,
  name: 'Martin'
}

type Person = typeof obj;

const obj2: Person = {
  id: 2,
  name: 'Lukas'
}

keyof

  • gets keys from an object type
const obj = {
  id: 1,
  name: 'Martin'
}

type Person = typeof obj;

 // 'id' | 'name'
const key: keyof Person = 'id';

Intersection type

  • combines multiple types
type Person = {
  id: number;
  name: string
}

type Developer = {
  os: 'Linux' | 'macOS' | 'Windows';
}

const someone: Person & Developer = {
  id: 5,
  name: 'George',
  os: 'Linux'
}

Generics

let value: Array<number> = [5, 2, 4];

// generic function
function print<T>(item: T) {
  console.log(item);
  return item;
}
print<string>('hi');
let x = print(4); // x will be of type number

Create generic class

class Bucket<T> {
  items: T[] = [];

  insert(item: T) {
    this.items.push(item);
  }
}

let myBucket = new Bucket<string>();
myBucket.insert('hello');

Create a map

  • store data under keys
  • methods
    • set(key, value)
    • get(key)
    • has(key)
    • entities() - gives all keys
    • print() - prints everything inside the map

Utility classes

Partial<>

interface Person {
    age: number;
    name: string;
}

let obj: Partial<Person> = {
    age: 32
}

  • makes all keys optional

Pick<>

interface Person {
    age: number;
    name: string;
    height: number;
}

let obj: Pick<Person, 'age' | 'name'> = {
    age: 32,
    name: 'Martin'
}

  • object with only certain keys

Omit<>

interface Person {
    age: number;
    name: string;
    height: number;
}

let obj: Omit<Person, 'height'> = {
    age: 32,
    name: 'Martin'
}

  • object without certain keys

Exclude<>

type Animal = Dog | Turtle | Shark;
let obj: Exclude<Animal, Turtle>
// obj: Dog | Shark    

  • disallows certain type

Parameters<>

type AdditionFn = (a: number, b: number): number;
type Params = Parameters<AdditionFn>; // [number, number]

function add(a: number, b: number): number {
  return a + b;
}
type Param = Parameters<typeof add>; // [number, number]
  • for function parameters

Conditional type

type Coords3d = {
  x: number;
  y: number;
  z: number;
};
type Coords2d = {
  x: number;
  y: number;
};
type Sphere = {
  coords: Coords3d;
};
type Circle = {
  coords: Coords2d;
};
type Shape = Sphere | Circle;
function placeRandomly<T extends Shape>(
  shape: T
): T extends Sphere ? Coords3d : Coords2d {
  ...
}

placeRandomly({ coords: { x: 1, y: 2 } });

Create type

  • for a function which removes key from object
  • inputs:
    • object
    • key to be removed
  • output
    • object without the key
function removeKey(obj, key) {
  const duplicate = { ...obj };
  delete duplicate[key];
  return duplicate;
}

Decorators

@Component({
    selector: 'app-home',
    template: '<h1>Hello</h1>'
})
class HomeComponent {
}
  • not part of JS yet (stage 2)
  • adds metadata
  • modifies what follows on next line
Made with Slides.com