TYPESCRIPT

 

by Kamil Lolo

INTRODUCTION

warning

W prezentacji mogą się pojawić braki w polskich znakach, ponglish, błędy składniowe.  Wszystkich wrażliwych proszę o opuszczenie show przed zobaczeniem kontentu.  

Czym jest Typescript?

Czym jest TYpescript?

  • Język programowania stworzony przez Microsoft w 2012
  • Rozszerzenie języka javascript
  • Poprawny program js jest poprawnym programem ts
  • Kompiluje się do javascript
  • Może się kompilować do kilku różnych wersji js (np. ES3, ES6 )
  • Aktualna wersja 2.7
  • Statycznie typowany
  • Zorientowany obiektowo

CO NAM DAJE TS?

  • możliwość programowania obiektowego (klasy, interfejsy)
  • statyczne typowanie
  • enumy
  • programowanie generyczne
  • moduły
  • dekoratory
  • możliwość używania najnowszych elementów języka nie zależnie od przeglądarki

Kompilacja kodu

Jak zaczac?

1. Zainstaluj node.js oraz npm

2. Uruchom: npm install typescript -g

3. Włącz ulubione IDE

KOmpilacja kodu

W celu skompilowaniu kodu wystarczy uruchomić polecenie:

 

tsc helloWorld.ts

 

Wygeneruje to plik o takiej samej nazwie ale z rozszerzeniem js, w którym znajdzie się wygenerowany kod. 

Hello WORLD

function helloWorld(userName: string): void {
    console.log(`Hello ${userName}`);
}

helloWorld('Kamil');
function helloWorld(userName) {
    console.log("Hello " + userName);
}
helloWorld('Kamil');

Podczas kompilacji usuwane są wszystkie elementy ts, niewspierane przez js. Kod można uruchomić za pomocą node.js

konfiguracja kompilatora

  • konfigurację kompilatora dostarcza się poprzez flagi command line bądź też w pliku tsconfig.json
  • w przypadku uruchomienia polecenia tsc bez parametrów kompilator szuka pliku tsconfig.json w górę drzewa folderów
  • kiedy podamy jako parametr plik do kompilacji jako parametr, to tsconfig.json jest ignorowany
{
    "compilerOptions": {
        "module": "system",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "outFile": "./built/tsc.js",
        "sourceMap": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts"
    ]
}

uzycie konfiguracji kompilatora

(function Application() {
    playDemo();
})();
(function Application() {
    playDemo();
})();

function playDemo() {
    console.log('Hello world');
}
//# sourceMappingURL=index.js.map
function playDemo() {
    console.log('Hello world');
}
{
    "compilerOptions": {
        "module": "system",
        "noImplicitAny": true,
        "removeComments": true,
        "preserveConstEnums": true,
        "outFile": "./build/index.js",
        "sourceMap": true
    },
    "include": [
        "src/**/*"
    ],
    "exclude": [
        "node_modules",
        "**/*.spec.ts"
    ]
}

Demo.ts

tsconfig.json

Application.ts

index.js

uzycie konfiguracji kompilatora

Inne przydatne parametry

parametr opis
charset The character set of the input files.
diagnostics Show diagnostic information.
disableSizeLimit Disable size limitation on JavaScript project.
newLine Use the specified end of line sequence to be used when emitting files: "crlf"(windows) or "lf" (unix).”
noImplicitAny Raise error on expressions and declarations with an implied any type.
noImplicitReturns Report error when not all code paths in function return a value.
noUnusedLocals Report errors on unused locals.
noUnusedParameters Report errors on unused parameters.

Statyczne typowanie

Co to jest statyczne typowanie?

  • Jest to nadawanie typów zmiennym w czasie kompilacji programu
  • Pozwala wykryć więcej błędów w czasie kompilacji
  • Wadą jest konieczność pisania typów
  • Przeciwieństwo typowania dynamicznego, gdzie typy są nadane w trakcie działania programu
  • W dynamicznym typowaniu błędy mogą pojawić się późno i być trudne do analizy
kamil@lolcio:~$ node ./demo1.ts
/home/kamil/demo1.ts:14
transfer();
^

TypeError: transfer is not a function
    at Object.<anonymous> (/home/kamil/demo1.ts:14:1)
    at Module._compile (module.js:573:30)
    at Object.Module._extensions..js (module.js:584:10)
    at Module.load (module.js:507:32)
    at tryModuleLoad (module.js:470:12)
    at Function.Module._load (module.js:462:3)
    at Function.Module.runMain (module.js:609:10)
    at startup (bootstrap_node.js:158:16)
    at bootstrap_node.js:578:3
let transfer = function() {
    console.log('send transfer');
}

// ... many lines of codes
transfer = {
    amount: 1.00,
    title: 'unfortunate tax..'
}

// ... many lines of codes
transfer();

Co to jest statyczne typowanie?

  • Kompilator ts kontroluje nasz kod i nie pozwala wykorzystać błędnego typu
  • Błędy są wykrywane od razu na etapie kompilacji
  • Dzięki statycznemu typowaniu js może być troszkę bliższy programistą Javy
kamil@lolcio:~$ tsc ./demo1.ts
demo1.ts(7,5): error TS2322: Type '{ amount: number; title: string; }' 
    is not assignable to type '() => void'.

  Object literal may only specify known properties,
     and 'amount' does not exist in type '() => void'.
function getWelcomeMessage(): string {
    return ``;
}

function playDemo() {
    let result = getWelcomeMessage();
    result = 1; // error: Type number is not assignable to type string
}

Podstawowe typy w TS

  • boolean - ma wartości true/false. Nie mylić z Boolean
  • number -  jest zmiennoprzecinkowy
  • string - tworzony poprzez ', ", lub `
  • tablica - tworzona za pomocą [] lub generycznego obiektu Array
  • tuples - tablica stałej długości z elementami określonego typu
  • enum - typ wyliczeniowy
  • any - każdy dowolny typ
  • void - nic
  • never - typ mówi o tym że funkcja nic nie zwróci
let sentence: string = `Hello, my name is ${ fullName }.`; // 1
let list: number[] = [1, 2, 3]; // 2
let list: Array<number> = [1, 2, 3]; // 3

let tuple: [string, number] = ["hello", 10]; // 4

enum Color {Red, Green, Blue} // 5
let colors: Color[] = [Color.Red, Color.Green];

Jak uzywac typow w ts?

Typów w ts można używać:

  • w deklaracji zmiennych
  • w deklaracji typów parametrów
  • w określaniu typu zwracanego z metody
  • przy określaniu typu pól w klasie
function foo(x:number|string) : string {
    return `x = ${x}`;
}

const someX: number = 12;
const resultForNumber: string = foo(someX);
const resultForString: string = foo("2");

console.log(resultForNumber);
console.log(resultForString);

unia typów

ENUM

  • pozwala na zdefiniowanie zbioru stałych o określonej wartości
  • podobnie jak w C++ stałe w enum przechowują liczby
  • domyślne wartości to 0, 1, 2 itd.
  • wartości enum mogą być obliczane w trakcie kompilacji
  • w wygenerowanych kodzie jest mapowanie name -> value oraz odwrotne
const enum Color {
    GREEN,
    RED,
    BLUE = 2*2
}

function playDemo() {
    let color: Color = Color.BLUE;
    console.log(color);
}


const enum Animal {
    DOG = 12,
    CAT = 7
}
(function (Color) {
    Color[Color["GREEN"] = 0] = "GREEN";
    Color[Color["RED"] = 1] = "RED";
    Color[Color["BLUE"] = 4] = "BLUE";
})
(Color || (Color = {}));

(function (Animal) {
    Animal[Animal["DOG"] = 12] = "DOG";
    Animal[Animal["CAT"] = 7] = "CAT";
})(Animal || (Animal = {}));

ANY

  • określa że zmienna/parametr jest dowolnego typu
  • stworzone z myślą o integracji z istniejącym kodem, trzecimi bibliotekami
  • nie należy używać w nowym kodzie, żeby ułatwić sobie pracę
  • --noImplicitAny sprawi że kompilator nie pozwoli używać any w kodzie
  • pozwala ominąć problemy z użyciem Object jako typu zmiennej
let notSure: any = 4;
notSure.ifItExists(); // okay, ifItExists might exist at runtime
notSure.toFixed(); // okay, toFixed exists (but the compiler doesn't check)

let prettySure: Object = 4;
prettySure.toFixed(); // Error: Property 'toFixed' doesn't exist on type 'Object'.

VOID

  • w przypadku funkcji, typ zwracany void zabrania zwrócenia danych z metody
  • dozwolone jest jednak zwrócenie null i undefined
  • w przypadku zmiennych blokuje możliwość ustawienia innej wartości niż null/undefined
function voidFun(): void {
    return null; // ewentualnie undefined
}

let y: void = undefined;
let x: void = voidFun();
console.log(x);

tuples

  • jest to mechanizm pozwalający definiować tablice w których określamy typy poszczególnych elementów
  • dalsze elementy tablicy nie są sprawdzane

let tupleNumber: [number, string] = [1, 'jakis string'];
tupleNumber[0] = ''; // error
tupleNumber[1] = 1; // error

// dalsze elementy tablicy nie są sprawdzane
tupleNumber[2] = 1;
tupleNumber[3] = 'text';

NEVER

  • Oznacza się nim funkcje które nic nie mogą zwrócić.

  • W  przeciwieństwie do void,  funkcja nie może

    ani posiadać instrukcji return ani się kończy w prawidłowy sposób.

  • Metody oznaczone jako never mogą w środku zawierać jedynie nieskończone pętle, bądź też wyrzucać wyjątek,

    zamiast normalnego wyjścia z metody.

     

function neverFun(): never {
    while (true) {
    }
}


function neverFunWithThrow(): never {
    console.log('neverFunWithThrow');
    throw new Error('this is never enging function');
}

Parametry opcjonalne i domyslne

  • js nie sprawdza liczby parametrów, stąd też nie ma przeciążania metod

  • parametry opcjonalne oraz domyślne są dostępne również w ES6

  • TS również dopuszcza użycie rest parameters

function getWelcomeMessage(userName: string = 'noname', formal?: boolean): void {
    console.log(`${formal ? 'Welcome mr.' : 'Hello'} ${userName}`);
}

function getWelcomeMessageForMultipleUser(formal: boolean = false, ...users: string[]): void {
       console.log(`${formal ? 'Welcome mr.' : 'Hello'} ${users}`);
}

function playDemo() {
    getWelcomeMessage('Kamil', true); // Welcome mr. Kamil
    getWelcomeMessage('Kamil'); // Welcome mr. Kamil
    getWelcomeMessage(); // Hello noname

    getWelcomeMessageForMultipleUser(true, 'Kamil', 'Bartek'); // Welcome mr. Kamil,Bartek
}

type

  • za pomocą słowa kluczowego type jesteśmy w stanie zrobić alias dla istniejącego typu

  • używane jako forma dokumentacji

  • ułatwia pracę w przypadku typów generycznych

  • działanie bardzo podobne do interfejsów jednak nie moga być implementowane ani rozszerzane

type Name = string;
type NameResolver = () => string;
type NameOrResolver = Name | NameResolver;
function getName(n: NameOrResolver): Name {
    if (typeof n === "string") {
        return n;
    }
    else {
        return n();
    }
}

type Alias = { num: number }
interface Interface {
    num: number;
}
declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;

funkcje jako typ

  • w js funkcje traktowane są na równi z innymi obiektami

  • funkcję można przypisywać do zmiennych

  • przy deklaracji typów można używać funkcji

interface SearchFunc {
    foo(source: string, subString: string): boolean;
}

class User {
    constructor(
        public name: string,
        public age: number ) { }
}

let isUserAdult: (user: User) => boolean = user => user.age > 18;

let users: User[] = [
    new User('Zenek', 12),
    new User('Martin', 26),
    new User('Karol', 34)
];

users
    .filter(isUserAdult)
    .map(user => `${user.name}=${user.age}`)
    .forEach((element, index) => console.log(`${index + 1}.${element}`));

// 1.Martin=26
// 2.Karol=34

class and interfaces

Tworzenie wlasnych typow

  • TS pozwala na programowanie obiektowe z wykorzystaniem klas
  • bardziej intuicyjne niż programowanie w oparciu o prototype
  • ES6 również wprowadza mechanizm klas
  • obiektowość w TS pozwala na korzystanie z interfejsów,  zakresów widoczności konstruktorów, pól statycznych, dziedziczenia oraz polimorfizmu
  • składania zbliżona do Javy

Interfejsy

  • najprostszy sposób na wymuszenie sprawdzania czy przekazany obiekt ma odpowiednią strukturę
  • mogą występować w formie anonimowej
interface Food {
    name: string;
    calories: number;
}

function speak(food: Food): void{
  console.log("Our " + food.name + " has " + food.calories + " calories.");
}

speak({
  name: "ice cream", 
  calories: 200
});


function printLabel(labelledObj: { label: string }) {
    console.log(labelledObj.label);
}

let myObj = {size: 10, label: "Size 10 Object"};
printLabel(myObj);

Interfejsy

  • w interfejsach oraz w klasach możemy zabezpieczyć pola przed zapisem za pomocą readonly
  • w interfejsach pola możemy deklarować jako nie wymagane
  • nie wymagane pola mogą zostać pominięte, jednak przekazywany obiekt nie może mieć innych właściwości niż te zadeklarowne w interfejsie
interface SquareConfig {
    readonly color?: string;
    readonly width?: number;
}

interface SquareConfig {
    color?: string;
    width?: number;
}

function createSquare(config: SquareConfig): { color: string; area: number } {
    // ...
    return null;
}

let mySquare = createSquare({ colour: "red", width: 100 }); // error: colour is not color
let mySquare = createSquare({ colour: "red", width: 100 } as SquareConfig); 

indexowane typy

  • TS pozwala tworzyć tablice gdzie kluczem jest liczba,
  • użycie interfejsu pozwala zdefiniować również typ klucza w tablicy jako string
let userAge: number[] = [];
userAge[0] = 27; // ok
userAge['kamil'] = 27; // error


interface IUserMap {
    [key: string]: any; // jak pozbyć się any i wstawić typ??
}

let userAge: IUserMap = [];
userAge[0] = 27; // ok
userAge['kamil'] = 27; // ok

dodatkowe elementy interfejsow

  • interfejsy mogą rozszerzać inne interfejsy
  • interfejsy mogą zawierać pola
  • mogą dziedziczyć po klasach (dziedziczy pola a nie implementacje)
interface Shape {
    color: string;
}

interface Square extends Shape {
    sideLength: number;
}

let square = <Square>{};
square.color = "blue";
square.sideLength = 10;

Tworzenie wlasnych typow

abstract class User {
    private userName: string;
    private age: number;

    constructor(userName: string, age: number) {
        this.userName = userName;
        this.age = age;
    }

    abstract showUserInfo(): void;
}

class Employee extends User {
    private salary: number;

    constructor(userName: string, age: number, salary: number) {
        super(userName, age);
        this.salary = salary;
    }

    showUserInfo(): void {
        console.log(this);
    }
}

let firstUser: User = new Employee('Kamil', 18, 1200);
firstUser.showUserInfo(); // Employee { userName: 'Kamil', age: 18, salary: 1200 

Jaki kod wygeneruje kompilator?

var User = (function () {
    function User(userName, age) {
        this.userName = userName;
        this.age = age;
    }
    return User;
}());

var Employee = (function (_super) {
    __extends(Employee, _super);

    function Employee(userName, age, salary) {
        var _this = _super.call(this, userName, age) || this;
        _this.salary = salary;
        return _this;
    }

    Employee.prototype.showUserInfo = function () {
        console.log(this);
    };

    return Employee;
}(User));

var firstUser = new Employee('Kamil', 18, 1200);
firstUser.showUserInfo(); // Employee { userName: 'Kamil', age: 18, salary: 1200 

Pola w klasie

  • Każde pole w klasie oraz funkcja może mieć określony zakres widoczności
  • Domyślnie zakres to public, do dyspozycji mamy również protected i private
  • Pola mogą być również zabezpieczone przed zapisem - readonly
  • przy deklaracji pól możemy używać modyfikatora static
class Octopus {
    readonly name: string;
    readonly numberOfLegs: number = 8;
    static readonly kind: string = 'Animal';
   
    constructor (readonly theName: string) {
        this.name = theName;
    }
}

console.log(Octopus.kind);
let dad = new Octopus("Man with the 8 strong legs");
dad.name = "Man with the 3-piece suit"; // error! name is readonly.

get/set

  • Ts daje nam możliwość definiowania setterów i getterów dla pól w klasi
let passcode = "secret passcode";

class Employee {
    private _fullName: string;

    get fullName(): string {
        return this._fullName;
    }

    set fullName(newName: string) {
        if (passcode && passcode == "secret passcode") {
            this._fullName = newName;
        }
        else {
            console.log("Error: Unauthorized update of employee!");
        }
    }
}

let employee = new Employee();
employee.fullName = "Bob Smith";
if (employee.fullName) {
    console.log(employee.fullName);
}

Czy warto tego używać?

this

class User {
    constructor(private readonly account: string) {

    }

    getUserInfoFactory(): () => string {
        return function (): string {
            return `account: ${this.account}`;
        }
    }
}

class Transfer {
    private userInfo: () => string;
    constructor(private readonly user: User) {
        this.userInfo = this.user.getUserInfoFactory();
    }

    makeTransfer() {
        console.log(this.userInfo());
    }

}

const user: User = new User('11-2222-3333-4444-5555-66');
new Transfer(user).makeTransfer(); // co zostanie wypisane na ekranie?

this

class User {
    constructor(private readonly account: string) {

    }

    getUserInfoFactory(): () => string {
        return (): string => {
            return `account: ${this.account}`;
        }
    }
}

class Transfer {
    private userInfo: () => string;
    constructor(private readonly user: User) {
        this.userInfo = this.user.getUserInfoFactory();
    }

    makeTransfer() {
        console.log(this.userInfo());
    }

}

const user: User = new User('11-2222-3333-4444-5555-66');
new Transfer(user).makeTransfer(); // account: 11-2222-3333-4444-5555-66

-zmienna this jest ustawiana w momencie kiedy funkcja jest wywoływana

-arrow pozwala zapamiętać this z miejsca gdzie funkcja był tworzona

 

type compability

interface Named {
    name: string;
}

class Person {
    name: string;
}

let p: Named;
// OK, because of structural typing
p = new Person();


let x: Named;
// y's inferred type is { name: string; location: string; }
let y = { name: "Alice", location: "Seattle" };
x = y;



listenEvent(
    EventType.Mouse, 
    (e: Event) => console.log((<MouseEvent>e).x + "," + (<MouseEvent>e).y)
);

-TS potrafi weryfikować zgodność obiektów na podstawie zawartości obiektów

 

type compability

interface Empty<T> {
}
let x: Empty<number>;
let y: Empty<string>;

x = y;  // okay, y matches structure of x
interface NotEmpty<T> {
    data: T;
}
let x: NotEmpty<number>;
let y: NotEmpty<string>;

x = y;  // error, x and y are not compatible

decorators

Co to jest?

  • funkcja przyjmująca inną funkcję, w celu roszerzenia jej działania w sposób deklaratywny
  • odpowiednik wzorca dekorator z OOP
  • mechanizm przypominający adnotacje, ale z większymi możliwościami
  • eksperymentalna funkcja (dostępna w ES 7)
  • Dekoratory często wymagają użycia call/apply oraz zaawansowanej wiedzy o js

Rodzaje dekoratorow

  • Method decorator
  • Property decorator
  • Class decorator
  • Parameter decorator

method decorator

  • Pozwala kontrolować dane wejściowe i wyjściowe do funkcji
  • Dekoruje wywołanie funkcji o dodatkową logikę
  • Parametry dekoratora:
  1. target - obiekt, który jest dekorowany
  2. key - nazwa metody, która jest dekorowana
  3. descriptor - deskryptor obiekty (Object.getOwnPropertyDescriptor())
function foo() {}
foo.x = 123;
console.log(Object.getOwnPropertyDescriptor(foo,'x'))

Result:
{ 
  value: 123,
  writable: true,
  enumerable: true,
  configurable: true 
}

method decorator

class User {
    public userName: string;

    constructor(userName?: string) {
        this.userName = userName;
    }

    @Deprecated
    setUserName(userName: string): void {
        this.userName = userName;

    }
}

new User().setUserName('Kamil'); // Using deprecated API: User.makeTransfer
function Deprecated(target: any, key: string, descriptor: any) {
    return {
        value: function (...args: any[]) {
            console.warn(`Using deprecated API: ${target.constructor.name}.${key}`);
            return descriptor.value.apply(this, args);
        }
    };
}

Property decorator

class User {
    @logProperty
    public userName: string;
}

const user: User = new User();
user.userName = 'Kamil'; // Set: userName => Kamil
function logProperty(target: any, key: string) {
    // property value
    let _val = this[key];

    // property getter
    const getter = function () {
        console.log(`Get: ${key} => ${_val}`);
        return _val;
    };

    // property setter
    const setter = function (newVal: any) {
        console.log(`Set: ${key} => ${newVal}`);
        _val = newVal;
    };

    // Delete property.
    if (delete this[key]) {
        // Create new property with getter and setter
        Object.defineProperty(target, key, {
            get: getter,
            set: setter,
            enumerable: true,
            configurable: true
        });
    }
}

Class decorator

@LogClass('Creating class with parameter:')
class Person {

    public name: string;
    public surname: string;

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

var p = new Person("remo", "jansen");
function LogClass(message: string): any {
    return function logClass(target: any) {
        // save a reference to the original constructor
        var original = target;

        // a utility function to generate instances of a class
        function construct(constructor: any, args: any) {
            var c: any = function () {
                return constructor.apply(this, args);
            }
            c.prototype = constructor.prototype;
            return new c();
        }

        // the new constructor behaviour
        var f: any = function (...args: any[]) {
            console.log(message + args);
            return construct(original, args);
        }

        // copy prototype so intanceof operator still works
        f.prototype = original.prototype;

        return f;// return new constructor (will override original)
    }
}

Decorator factory

do czego mozna uzywac dekoratorow?

import { Component, ElementRef, Input, OnInit, OnDestroy } from '@angular/core';
import { ModalService } from '../_services/index';
 
@Component({
    moduleId: module.id.toString(),
    selector: 'modal',
    template: '<ng-content></ng-content>'
})
export class ModalComponent implements OnInit, OnDestroy {
    @Input() id: string;
    private element: JQuery;
 
    constructor(private modalService: ModalService, private el: ElementRef) {
        this.element = $(el.nativeElement);
    }
 
    ngOnInit(): void {
        this.modalService.add(this);
    }
 
    ngOnDestroy(): void {
        this.element.remove();
    }
}

module

moduly w ts

  • TS pozwala dzielić aplikację na moduły
  • moduły pozwalają nadawać zakresy zmiennym i typom
  • zwiększa to reużycie kodu np. w różnych projektach
  • możemy wykorzystać dowolny mechanizm ładowania modułów w runtime (AMD, UMD, CommonJS, ES 6 module)
  • forma enkapsulacji, pozwala uniknąć kolizji nazw
  • ułatwia testowanie

module Transfer { // moduł wewnętrzny - tożsamy z namespace
    export class Account { }
}


let account: Transfer.Account = new Transfer.Account();

przyklady wykorzystania modulow

// 1
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();

// 2
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
let myValidator = new ZCV();


// 3
namespace Shapes {
    export namespace Polygons {
        export class Triangle { }
        export class Square { }
    }
}

import polygons = Shapes.Polygons;
let sq = new polygons.Square(); // Same as 'new Shapes.Polygons.Square()'

namespace w kilku plikach

Validation.ts:

namespace Validation {
    export interface StringValidator {
        isAcceptable(s: string): boolean;
    }
}


LettersOnlyValidator.ts: 

/// <reference path="Validation.ts" />

namespace Validation {
    const lettersRegexp = /^[A-Za-z]+$/;
    export class LettersOnlyValidator implements StringValidator {
        isAcceptable(s: string) {
            return lettersRegexp.test(s);
        }
    }
}
  • Przestrzenie nazw mogą być rozdzielone na kilka plików z wykorzystaniem reference path
  • tzw. triple slash musi znajdować się na początku pliku

generics

generyczne metody

  • TS pozwala tworzyć generyczne funkcje oraz klasy 
  • dzięki generykom  nie musimy korzystać z any
// 1
function identity(arg: number): number {
    return arg;
}

// 2
function identity(arg: any): any {
    return arg;
}

// 3
function identity<T>(arg: T): T {
    return arg;
}

// 4
let output = identity<string>("myString");  // type of output will be 'string'
let output = identity("myString");  // type of output will be 'string'

generyczne metody

  • generyczne metody można przypisywać do zmiennych
  • dozwolone jest również zawężanie typów generycznych
// 1
function identity<T>(arg: T): T {
    return arg;
}

let myIdentity: <T>(arg: T) => T = identity;

// 2
interface Lengthwise {
    length: number;
}

function loggingIdentity<T extends Lengthwise>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

zawezanie generykow poprzez wlasciwosci

function getProperty<T, K extends keyof T>(obj: T, key: K) {
    return obj[key];
}

let x = { a: 1, b: 2, c: 3, d: 4 };

getProperty(x, "a"); // okay

// error: Argument of type 'm' isn't 
// assignable to 'a' | 'b' | 'c' | 'd'
getProperty(x, "m");

generyczne klasy

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}

let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };

class Animal {
    numLegs: number;
}

class Lion extends Animal {
    keeper: ZooKeeper;
}

function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}

createInstance(Lion).keeper.nametag;  // typechecks!

Czy potrzebuje TS?

TAK!

  • chcę uniknąć błędów pojawiających się przy dynamicznym typowaniu
  • jestem programistą języka silnie typowanego np. Javy i przesiadam się na js
  • chce używać Angular2+
  • chce łatwiejszy w utrzymaniu kod i bardziej przewidywalny

NIE!

  • programowanie obiektowe możliwe jest od ES6
  • dodatkowy narzut przy budowaniu projektu
  • konieczność pisania większej ilości kodu
  • jestem ekspertem w js
  • rozwijam istniejącą aplikację w AngularJS
  • wszystko ma być na wczoraj w projekcie i nie ma czasu na naukę nowego języka

dobre praktyki

uzywaj const i let zamiast var

many common problems in JavaScript are alleviated by using let, so you should use it instead of var whenever possible.

- dokumentacja TypeScript

Nigdy nie uzywaj wrapperow na typy proste

Don’t ever use the types Number, String, Boolean, or Object. These types refer to non-primitive boxed objects that are almost never used appropriately in JavaScript code.

- dokumentacja TypeScript

Zamiast przeciazac metody korzystaj z uni typow

Don’t write overloads that differ by type in only one argument position:

/* WRONG */
interface Moment {
    utcOffset(): number;
    utcOffset(b: number): Moment;
    utcOffset(b: string): Moment;
}

/* OK */
interface Moment {
    utcOffset(): number;
    utcOffset(b: number|string): Moment;
}

przeciazone metody sortuj od tych najbardziej precyzyjnych do ogolnych

TypeScript chooses the first matching overload when resolving function calls. When an earlier overload is “more general” than a later one, the later one is effectively hidden and cannot be called.

- dokumentacja TypeScript

/* OK */
declare function fn(x: HTMLDivElement): string;
declare function fn(x: HTMLElement): number;
declare function fn(x: any): any;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: string, :)
/* WRONG */
declare function fn(x: any): any;
declare function fn(x: HTMLElement): number;
declare function fn(x: HTMLDivElement): string;

var myElem: HTMLDivElement;
var x = fn(myElem); // x: any, wat?

zamiast przeciazac uzywaj opcjonalnych parametrow

/* OK */
interface Example {
    diff(one: string, two?: string, three?: boolean): number;
}
/* WRONG */
interface Example {
    diff(one: string): number;
    diff(one: string, two: string): number;
    diff(one: string, two: string, three: boolean): number;
}

podsumowanie

https://www.typescriptlang.org/index.html

Najlepsze źródło informacji:

import {Component, OnInit, EventEmitter} from '@angular/core';

@Component({
  selector: 'app-actions',
  templateUrl: './actions.component.html',
  styleUrls: ['./actions.component.css']
})
export class ActionsComponent implements OnInit {
  modalActions = new EventEmitter<any>();
  isFormValid = false;

  constructor(private refreshHistoryObserver: RefreshHistoryObserver) {
    
  }
}

Prezentacja powinna dać podstawowe informacje potrzebne do zaczęcia zabawy pracy z Angular 5

KONIEC

Introduction to typescript

By Kamil Lolo

Introduction to typescript

  • 705