Lekcja 6. TypeScript — podstawy języka: typy, interfejsy i klasy
ŚredniPo co się tego uczymy?
Angular pisze się w TypeScript, więc zanim zbudujesz cokolwiek poważniejszego niż jeden przycisk, musisz poznać ten język. Dobra wiadomość: jeśli znasz C#, TypeScript będzie Ci się wydawał bardzo znajomy — silne typowanie, klasy, interfejsy — to te same koncepcje, które poznałeś w dziale programowania obiektowego, tylko zapisane trochę inną składnią i finalnie kompilowane do zwykłego JavaScriptu.
Teoria
Czym jest TypeScript? To język stworzony przez Microsoft jako NADZBIÓR JavaScriptu — każdy poprawny kod JavaScript jest jednocześnie poprawnym kodem TypeScript, ale TypeScript DODAJE do niego system typów (podobny do tego, co znasz z C#) i inne udogodnienia (interfejsy, klasy w nowoczesnym stylu, generyki). Przeglądarki NIE rozumieją TypeScriptu bezpośrednio — kod .ts jest KOMPILOWANY (a właściwie "transpilowany") do zwykłego JavaScriptu, zanim trafi do przeglądarki. Ten proces dzieje się automatycznie w tle, gdy uruchamiasz projekt Angular.
Dlaczego typy są tak ważne? W zwykłym JavaScript zmienna może w jednej linijce być liczbą, a w następnej stringiem — co prowadzi do trudnych do wyśledzenia błędów. TypeScript pozwala zadeklarować, JAKIEGO typu jest dana zmienna, i zgłasza błąd KOMPILACJI (czyli ZANIM program się uruchomi), jeśli spróbujesz przypisać niewłaściwą wartość — dokładnie ta sama korzyść, którą daje silne typowanie w C#.
| Konstrukcja TypeScript | Odpowiednik w C# | Przykład |
|---|---|---|
let x: number = 5; |
int x = 5; (choć TS ma tylko jeden typ liczbowy) |
liczby całkowite i zmiennoprzecinkowe to w TS TEN SAM typ number |
let s: string = "cześć"; |
string s = "cześć"; |
teksty |
let czyGotowe: boolean = true; |
bool czyGotowe = true; |
wartości logiczne |
let lista: number[] = [1, 2, 3]; |
List<int> lista = new() {1,2,3}; |
tablice/listy |
interface Produkt { nazwa: string; cena: number; } |
podobne do klasy z samymi właściwościami (bez logiki) | opis KSZTAŁTU obiektu, bez implementacji |
class Produkt { ... } |
class Produkt { ... } |
klasa z polami I metodami — bardzo podobna składnia do C# |
function suma(a: number, b: number): number { ... } |
int Suma(int a, int b) { ... } |
funkcja z typowanymi parametrami i typem zwracanym |
Interfejsy — opis "kształtu" danych. W TypeScript interfejs (interface) opisuje, JAKIE właściwości musi mieć obiekt, bez definiowania żadnej logiki — bardzo przydatne do opisania struktury danych przychodzących z backendu (np. odpowiedzi JSON z Web API C#, pełne omówienie w lekcji 12). To trochę jak model danych z C# (klasa z samymi właściwościami), tylko bez potrzeby tworzenia instancji przez new.
Typy opcjonalne i null. Znak zapytania po nazwie właściwości (cena?: number) oznacza, że ta właściwość jest OPCJONALNA (może nie istnieć w ogóle) — podobna koncepcja do double? w C#, choć techniczne działanie różni się w szczegółach.
Konfiguracja kompilatora — plik tsconfig.json. Określa, jak ściśle TypeScript sprawdza typy, do jakiej wersji JavaScriptu kompiluje kod, i inne ustawienia — w projektach Angular jest generowany automatycznie przez Angular CLI, zwykle nie trzeba go ręcznie modyfikować na początku nauki.
Schemat
Plik .ts (TypeScript, pisany przez programistę)
│
│ kompilator TypeScript (tsc), uruchamiany automatycznie
│ przez Angular CLI (ng serve / ng build)
▼
Plik .js (zwykły JavaScript, zrozumiały dla przeglądarki)
│
▼
Wykonanie w przeglądarce użytkownika
Sprawdzanie typów - PRZED uruchomieniem:
let cena: number = "sto złotych"; ← BŁĄD KOMPILACJI, wykryty od razu
(string przypisany do number)
interface Produkt { class Produkt { (dla porównania: C#)
nazwa: string; public string Nazwa {get;set;}
cena: number; public double Cena {get;set;}
} }
(tylko OPIS kształtu danych) (klasa z polami i metodami)
Przykład z życia
Formularz rejestracji w aplikacji Angular oczekuje, że pole "wiek" będzie liczbą — dzięki TypeScript, próba przypisania do niego tekstu (np. przez błąd w kodzie, nie przez użytkownika — to inny temat, walidacja formularzy) zostanie wykryta ZANIM aplikacja w ogóle zostanie uruchomiona, a nie dopiero w trakcie działania u klienta, gdzie błąd byłby dużo trudniejszy do namierzenia.
modele.ts
// modele.ts - interfejsy opisujące kształt danych
export interface Produkt {
id: number;
nazwa: string;
cena: number;
dostepny?: boolean; // znak zapytania = pole opcjonalne
}
// Funkcja z typowanymi parametrami i typem zwracanym
function obliczRabat(cena: number, procent: number): number {
return cena - (cena * procent / 100);
}
const cenaPoRabacie = obliczRabat(100, 20); // 80
console.log(cenaPoRabacie);
klasa.ts
// klasa.ts - klasa TypeScript (podobna do klasy C#)
export class KontoBankowe {
private saldo: number;
constructor(saldoPoczatkowe: number) {
this.saldo = saldoPoczatkowe;
}
wplac(kwota: number): void {
this.saldo += kwota;
}
wyplac(kwota: number): boolean {
if (kwota > this.saldo) {
return false; // niewystarczające środki
}
this.saldo -= kwota;
return true;
}
pobierzSaldo(): number {
return this.saldo;
}
}
// Użycie:
const konto = new KontoBankowe(100);
konto.wplac(50);
const udaloSie = konto.wyplac(30);
console.log(konto.pobierzSaldo()); // 120
Komentarz i wyjaśnienie kodu
Zwróć uwagę na PRAWIE identyczną strukturę klasy KontoBankowe do tego, co pisałbyś w C#: pole prywatne (private saldo: number), konstruktor (constructor(...) zamiast nazwy klasy jak w C#), metody z typowanymi parametrami i typem zwracanym. Główne różnice składniowe: typ podaje się PO dwukropku (saldo: number, a nie number saldo), słowo kluczowe constructor zamiast powtórzenia nazwy klasy, oraz void jako typ zwracany dla metod niczego nie zwracających — dokładnie jak w C#.
export przed interface/class pozwala UŻYĆ tego elementu w INNYM pliku TypeScript przez import { Produkt } from './modele'; — to bezpośredni odpowiednik public na poziomie klasy w C#, tylko zorganizowany wokół PLIKÓW (moduły ES) zamiast przestrzeni nazw.
Ćwiczenie samodzielne
Utwórz plik .ts (może być poza projektem Angular, np. w prostym projekcie Node.js albo online w playground TypeScript na stronie typescriptlang.org) z interfejsem opisującym dowolny obiekt (np. Ksiazka z tytułem, autorem, rokiem wydania) i funkcją operującą na tablicy takich obiektów (np. filtrowanie książek wydanych po określonym roku).
Zadania do pracy własnej
Zdefiniuj interfejs
Uczen(imię, nazwisko, średnia ocen) i napisz funkcjęczyNaStypendium(uczen: Uczen): booleanzwracającątrue, jeśli średnia jest większa niż 4.75.Napisz klasę
Prostokatz polamiszerokosciwysokosc(typunumber), konstruktorem przyjmującym obie wartości, oraz metodamipole(): numberiobwod(): number— przetestuj ją tworząc kilka instancji z różnymi wymiarami.Wykorzystaj TypeScriptowe GENERYKI (składnia bardzo podobna do generyków C#:
class Stos<T> { ... }) do zaimplementowania prostej struktury danych "stos" (LIFO — Last In, First Out) z metodamidodaj(element: T): void,zdejmij(): T | undefinediczyPusty(): boolean— przetestuj ją zarówno na stosie liczb, jak i stosie stringów, korzystając z JEDNEJ, uniwersalnej klasy.
Typowe błędy
Ignorowanie błędów typów zgłaszanych przez kompilator ("przecież i tak działa w przeglądarce") — błędy typów w TypeScript są sygnałem, że kod praktycznie na pewno ma ukrytą pomyłkę logiczną, nawet jeśli akurat nie ujawnia się w bieżącym teście.
Nadużywanie typu any (oznaczającego "dowolny typ, bez sprawdzania") jako "łatwego wyjścia" z błędu kompilacji — to rezygnacja z GŁÓWNEJ zalety TypeScriptu; lepiej poświęcić chwilę na znalezienie poprawnego typu.
Mylenie interfejsu (interface, tylko opis kształtu danych) z klasą (class, pełna implementacja z metodami) — interfejsu NIE MOŻNA utworzyć przez new, bo nie zawiera żadnej logiki, tylko strukturę.
Nawiązanie do egzaminu zawodowego
To realizacja INF.04.7.2 (frameworki i języki aplikacji webowych, gdzie TypeScript jest nierozerwalnie związany z Angularem) — solidna znajomość podstaw TypeScript jest WARUNKIEM zrozumienia wszystkich kolejnych lekcji o Angularze (8-10), bo cała logika komponentów jest pisana właśnie w tym języku. Kolejna lekcja przechodzi do stylowania interfejsu — biblioteki Bootstrap.