Poznajemy interfejsy. Kolejna z lekcji „nudne początki TSa”, musimy się jakoś przez to przebić. Na pocieszenie dodam, że większość z tych tematów przekłada się 1 do 1 na takie języki jak C#.

Ok, rzecz pierwsza, czyli typ vs interfejs:

type Vehicle = {
    wheels: number;
    maker: string;
};

interface Vehicle {
    wheels: number;
    maker: string;
}

Żadnej różnicy nie ma na razie, ale gdybyśmy chcieli coś więcej, to z typu niewiele da się wycisnąć. Może to wydawać się mało oczywiste, ale interfejsy mogą też z siebie dziedziczyć:

interface Vehicle {
    wheels: number;
    maker?: string;
}

interface Car extends Vehicle {
    power: "gas" | "electricity";
}

interface Bicycle extends Vehicle {
    folding: boolean;
}

Tu mamy kilka rzeczy:

  • marker może być albo string, albo undefined (inaczej string | undefined)
  • Interfejsy dziedziczą z siebie
  • Car ma bardzo konkretny typ, nie string tylko konkretne stringi przyjmuje

Tu mamy przykład jak się implementuje interfejs w klasie:

interface IEmployee {
    empCode: number;
    name: string;
    getSalary:(empCode: number) => number;
}

class Employee implements IEmployee { 
    empCode: number;
    name: string;

    constructor(code: number, name: string) { 
        this.empCode = code;
        this.name = name;
    }

    getSalary(empCode:number):number { 
        return 20000;
    }
}

let emp = new Employee(1, "Steve");

Declaration merging to też ciekawa rzecz:

interface User{
    id: number;
    name: string;
    job: string;
    salary: string;
}

interface User {
    gender: string;
    isMarried: boolean;

}

I teraz chodzi tu o to, że nasz user ma mieć wszystkie pola z jednego i drugiego tak samo nazwanego interfejsu. Gdybyśmy chcieli mieć coś „do wyboru” to byśmy zastosowali dziedziczenie jak w poprzednim przykładzie.

Declaration merging jest po to, aby jakoś to zorganizować tak, aby nam było wygodnie, to nie ma związku z logiką. Jak chcesz logiki, to zrób interfejs z głównymi properties, zrób dziedziczące interfejsy, tam sobie różne rzeczy ustal i masz dwie opcje.

Declaration merging to nie dwie opcje tylko taki myk na podzielenie tego kodu. Tu mamy ciekawy przykład:

interface Document {
  createElement(tagName: any): Element;
}
interface Document {
  createElement(tagName: "div"): HTMLDivElement;
  createElement(tagName: "span"): HTMLSpanElement;
}
interface Document {
  createElement(tagName: string): HTMLElement;
  createElement(tagName: "canvas"): HTMLCanvasElement;
}   

I teraz efekt tego taki, że dostajemy taki interfejs:

interface Document {
  createElement(tagName: "canvas"): HTMLCanvasElement;
  createElement(tagName: "div"): HTMLDivElement;
  createElement(tagName: "span"): HTMLSpanElement;
  createElement(tagName: string): HTMLElement;
  createElement(tagName: any): Element;
}

I od razu muszę wspomnieć – nie ma w TS takiego przeładowywania jak w innych językach. Że jedna funkcja o jednej nazwie, ale różnej ilości argumentów, różnie nazwanych i tak dalej.

W TS możesz przeładowywać tylko w jeden sposób, to jest ta sama nazwa, ta sama ilość argumentów tak samo nazwanych, tylko typ inny. Zobaczmy taki kodzik po kompilacji:

function add(a, b) {
    return a + b;
}

To oznacza, że tych funkcji add możemy mieć ile chcemy, ale każda z nich będzie mieć 2 argumenty, a i b, tylko typy mogą być inne.

Ok, jeszcze jedna rzecz a mianowicie to (nie wiem jak to nazwać, indeksery?):

interface ArrLikeObj {
    [index: number] : number;
    length: number;
}

let obj : ArrLikeObj = {
    0: 1, 
    1: 2, 
    length: 2
}

Czyli może być artybut length, który zwraca number, a wszystko inne, to mają być klucze tylko numeryczne zwracające wartość tylko numeryczną.

To może generyczny odpowiednik teraz:

interface ArrLikeObjGen<T> {
    [index: number] : T;
    length: number;
}

let obj2 : ArrLikeObjGen<string> =  {
    0: 'a',
    1: 'b',
    2: 'c',
    length: 3
};

Ok więcej o interfejsach w następnych lekcjach. Po co są interfejsy to już mówiliśmy i widzieliśmy w PHP w projekcie framework MVC. To wszystko jest dla pisania lepszego kodu, dla errorów podczas kompilacji, na runtime to nie ma wpływu.