U srijedu je u sklopu CodeCamp-a održano predavanje na temu “Uvod u TypeScript”. Cilj predavanja bio je objasniti osnovne koncepte TypeScripta te ukazati na prednosti koje donosi njegovo korištenje.

TypeScript je superset JavaScripta - ne zamjena za njega. Kôd pisan u TypeScriptu se prvo prevodi u JavaScript, te se taj JavaScript kôd izvršava u browseru. Pri tome možemo odrediti koji JavaScript standard želimo, kao defaultni se koristi ES3. Da bismo koristili TypeScript, potrebno je instalirati njegov prevoditelj (compiler). On se instalira kao npm paket, a upute su dostupne na službenoj stranici TypeScripta. Prevođenje se radi jednostavnim pozivanjem na kôd koji želimo prevesti: tsc fileName.ts. Kao rezultat dobijemo fileName.js datoteku koja bi trebala biti što vjerniji prikaz TypeScript kôda iz originalne datoteke. Dakle, nema mijenjanja imena varijabli, funkcija i slično. Budući da je TypeScript superset JavaScripta te nam je u njemu dostupno sve što je dostupno u JavaScriptu, svaki JavaScript kôd je ujedno i validan TypeScript kôd. To znači da promjena ekstenzije datoteke iz .js u .ts neće utjecati na izvršavanje kôda. Taj dodatni korak prevođenja nam omogućuje da greške pronađemo već tijekom prevođenja, što je sigurno bolje nego tek nakon mjesec dana.

Ako nemate instaliran TypeScript na svom računalu, sve primjere možete pogledati na TypeScript Playground-u. S lijeve strane pišete TypeScript kôd, a s desne strane odmah možete vidjeti kako bi izgledao JavaScript kôd.

var vs. let

Kada je riječ o deklaraciji varijabli, u JavaScriptu smo do sada koristili riječ var. Međutim, var se ponekad ponaša na nama nelogičan način. Pogledajmo sljedeći primjer:

function f(shouldInitialize) {
    if (shouldInitialize) {
        var x = 10;
    }
    return x;
}

Ako bismo sada pozvali funkciju s parametrom true, rezultat bi bio 10. Ako dolazite iz C# ili Java svijeta to Vam se može činiti čudno. Ali, ako koristimo var, blok ne zatvara scope. Da bi se gornja funkcija ponašala na način na koji smo navikli, umjesto var trebamo koristiti let. Ta mala promjena rezultirat će greškom ako pokušamo pozvati funkciju f s bilo kojim parametrom jer x nije definiran izvan if bloka. Osim što ih je teško uočiti u većem kodu, ovakve greške mogu uzrokovati duga i bolna debugiranja.

Još jedna teško uočljiva greška bila bi sljedeća:

var x = 15;
var x = 'abc';

Pokušate li izvršiti ovaj kôd, vidjet ćete da je vrijednost varijable x sada 'abc'. Deklaracija varijable na ovaj način - s imenom koje već postoji - se ne tretira kao greška. Ako to učinimo u kôdu od nekoliko stotina linija, teško ćemo lako uočiti što nam stvara probleme. Rješenje je, ponovno, deklarirati varijablu koristeći let. Tada nije dozvoljeno deklarirati istu varijablu više puta. Zbog ovih, i sličnih trikova, u TypeScriptu se preporuča deklaracija varijabli korištenjem let-a. Međutim, ako imamo varijablu za koju znamo da joj se vrijednost neće mijenjati, preporuča se deklaracija korištenjem riječi const, npr. const a = 15. Na taj način njezinu vrijenost više nećemo moći mijenjati.

Tipovi varijabli

Jedna od odlika TypeScripta koja se najčešće koristi su tipovi varijabli. Korištenje tipova nije obavezno, ali pridonosi smanjenju broja grešaka za vrijeme pisanja kôda, te samoj čitljivosti kôda.

var foo: boolean = true;
var bar: number;

Tip varijable određujemo tako da nakon imena varijable pišemo dvotočku, te iza nje tip. Tako smo osigurali da varijabla ostaje istog tipa. Na primjer, ako bismo varijablama foo ili bar pokušali dodijeliti vrijednost 'abc', dobili bismo grešku jer niti jedna od njih nije tipa string ili any. any je tip koji varijabli dodjeljujemo ako želimo da se ponaša kao varijabla u JavaScriptu kako smo navikli, tj. ako želimo omogućiti dodjeljivanje bilo koje vrijednosti bilo kojeg tipa.

Predavanje

Funkcije

Kao što varijablama možemo odrediti tip, tako i funkcijama možemo odrediti tip podataka koje one vraćaju. Ako bismo sljedećoj funkciji pokušali proslijediti nešto što nije number ili ako bismo njezinu vrijednost pokušali pridružiti varijabli čiji tip nije number ili any, prevoditelj bi nam javio grešku.

function f(x: number): number {
    return x + x;
}

Također bi javio grešku i ako bismo ju pokušali pozvati bez parametara. Za razliku od JavaScripta gdje su svi parametri opcionalni, u TypeScriptu su svi parametri obavezni, osim ako ih ne označimo kao opcionalne. Pri tome u deklaraciji funkcije prvo stoje obavezni, a zatim opcionalni parametri. Parametar je označen kao opcionalan ako mu nakon imena stoji upitnik. Osim opcionalnih parametara, postoje i rest parametri; njih pišemo na kraju liste parametara i prepoznajemo po tri točke prije imena. Pogledajmo primjer funkcije koja ima jedan obavezan, jedan opcionalan i jedan rest parametar.

function f(x: number, y?: boolean, ...z: number[]): number {
    // ...
}

Jedan od možda najzanimljivijih značajki vezanih za funkcije u TypeScriptu je preopterećenje (overloading), što znači da možemo imati više funkcija s istim imenom, a različitim parametrima. Editori poput Visual Studia, Visual Studio Codea, Eclipsea i sličnih koji podržavaju TypeScript će nam prepoznati te funkcije, te ćemo imati intellisense kao kod overloadinga funkcija u npr. C#-u. Pišemo ih tako da prvo nižemo definicije funkcija koje želimo, te nakon toga pišemo implementaciju:

function f(x: number): number;
function f(x: string): string;
function f(x: any): any {
    if (typeof x === 'number') {
        return x + x;
    }
    else if (typeof x === 'string') {
        return x;
    }
}

Na ovaj način će nam biti dostupne dvije funkcije - jedna koja prima number i vraća number, te druga koja prima string i vraća string.

OOP

Objektno orjentirano programiranje u TypeScriptu je puno sličnije onom u C#-u nego onom u JavaScriptu. Osim klasa (class), dostupna su nam i sučelja (interface) te apstraktne klase (abstract class). Pogledajmo primjere:

interface IAnimal {
    pawCount: number;
    isDomestic: boolean;
    
    hasFourPaws(): boolean;
}

class Cat implements IAnimal {
    pawCount: number;
    isDomestic: boolean;
    
    hasFourPaws(): boolean {
        return true;
    }
}

Kada klasa implementira sučelje, tad mora implementirati i sva njegova svojstva (properties), kao gore navedena klasa Cat koja implementira sučelje IAnimal.

abstract class Animal {
    pawCount: number;
    isDomestic: boolean;
    
    hasFourPaws(): boolean {
        return this.pawCount === 4;
    }
    abstract isDog(): boolean;
}

class Dog extends Animal {
    isDog: boolean {
        return true;
    }
}

Kada klasa nasljeđuje apstraktnu klasu, mora implementirati samo ona svojstva koja su u apstraktnoj klasi označena kao apstraktna. Međutim, klasa može i implementirati apstraktnu klasu, ali tada mora implementirati sva njezine svojstva kao kada implementira sučelje. Također, dostupni su nam i indikatori dostupnosti: public, private i protected; te getteri i setteri. Pogledajmo primjer:

class Dog extends Animal {
    constructor() {
        // ako klasa nasljeđuje drugu klasu, tada u konstruktoru tada
        //na prvom mjestu mora biti poziv konstruktora klase koju nasljeđuje
        super();
    }
    private prop: boolean;
    
    get pawCount(): number {
        return this.pawCount;
    }
    set pawCount(value: number) {
        this.pawCount = value;
    }
    isDog: boolean {
        return true;
    }
}

let dog = new Dog();
dog.prop // prop nije dostupan jer je označen kao private

To nije sve

Budući da je predavanje zamišljeno kao uvod, prošli smo samo osnovne koncepte. Ima još zanimljivosti koje nismo pokrili, kao što su generičke funkcije (generic functions), enumi, pisanje async/await kôda… Treba napomenuti da je pisanje async/await kôda u TypeScript verzijama prije verzije 2.1 moguće samo ako prevedemo kôd u ES6. U verziji 2.1 je dostupan i ako prevedemo u ES3 i ES5. Zanimljivo je pogledati jednostavan primjer i vidjeti razliku TypeScript async kôda i JavaScript async kôda:

let fAsync = async function (x) {
    let a = await f1(x);
    await f2(x);
}

Iz ovih primjera vidimo da nam korištenje TypeScripta omogućuje brže i lakše debugiranje. Osim što nam njegov prevoditelj ukazuje na sintaktičke greške pri prevođenju kôda, njegove odlike mogu uvelike pridonijeti čitljivosti kôda - bilo da je riječ o manjim stvarima kao što su tipovi podataka, ili o većim, kao što je pisanje async/await kôda.

Ostale novosti

Vaše mišljenje nam je važno!

comments powered by Disqus