Kihagyás

Osztályok/Interface-ek

Interface

Az interface meghatározza, hogy egy objektumnak milyen tulajdonságokkal (és metódusokkal) kell rendelkeznie. Hasonló a Java interface-hez, de TypeScript-ben nem csak osztályokhoz, hanem objektumokhoz is használhatjuk.

Különbség a type alias és interface között

A type aliasokat tetszőleges típusokkal használhatod, akár primitívekkel is, míg interfészeket nem.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
interface Product {
    id: number;
    name: string;
    price: number;
    category: string;
}


const laptop: Product = {
    id: 1,
    name: "Laptop",
    price: 999.99,
    category: "Electronics"
};

function displayProduct(product: Product): void {
    console.log(`${product.name} (${product.category}) - $${product.price}`);
}

displayProduct(laptop);

Természetesen itt is használhatunk opcionális tulajdonságokat a ? segítségével.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Product {
    id: number;
    name: string;
    price: number;
    category: string;
    warranty?: number;
}


const laptop: Product = {
    id: 1,
    name: "Laptop",
    price: 999.99,
    category: "Electronics",
    warranty: 24
};

const coffe: Product = {
    id: 1,
    name: "Coffee",
    price: 10.99,
    category: "Coffee"
};

A különböző tulajdonságokat definiálhatjuk csak olvashatónak is, ilyen esetben az objektum létrehozása után a kezdeti érték nem változtatható meg.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
interface Product {
    readonly id: number;
    name: string;
    price: number;
    category: string;
    warranty?: number;
}


const laptop: Product = {
    id: 1,
    name: "Laptop",
    price: 999.99,
    category: "Electronics",
    warranty: 24
};

// laptop.id = 2; // TS2540: Cannot assign to id because it is a read-only property.
laptop.warranty = 12;

Metódusok az interface-ben

Az interface-ekben nem csak tulajdonságokat, hanem metódusokat is definiálhatunk. Ezek a metódusok csak a szignatúrát tartalmazzák, az implementációt az objektum vagy osztály adja majd meg.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
interface Product {
    readonly id: number;
    name: string;
    price: number;
    category: string;
    warranty?: number;

    getInfo(): string;
    applyDiscount(percentage: number): number;
}

const laptop: Product = {
    id: 1,
    name: "Laptop",
    price: 999.99,
    category: "Electronics",
    warranty: 24,

    getInfo(): string {
        return `${this.name} - $${this.price}`;
    },

    applyDiscount(percentage: number): number {
        return this.price * (1 - percentage / 100);
    }
};

console.log(laptop.getInfo());           // "Laptop - $999.99"
console.log(laptop.applyDiscount(10));   // 899.991

Interface kiterjesztése

Ha több hasonló interfészünk van, akkor kiterjeszthetjük őket az extends kulcsszóval. Ez hasznos, amikor közös tulajdonságokat szeretnénk kiemelni. Így megelőzhetjük a felesleges kódmásolatokat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
interface BaseProduct {
    readonly id: number;
    name: string;
    price: number;
}

interface Product extends BaseProduct {
    category: string;
    warranty?: number;
    getInfo(): string;
    applyDiscount(percentage: number): number;
}

const laptop: Product = {
    id: 1,
    name: "Laptop",
    price: 999.99,
    category: "Electronics",
    warranty: 24,
    getInfo(): string {
        return `${this.name} - $${this.price}`;
    },
    applyDiscount(percentage: number): number {
        return this.price * (1 - percentage / 100);
    }
};

Akár több interface-t is kiterjeszthetünk egyszerre, ha szeretnénk, ilyenkor az interfészeket csak simán felsoroljuk egymás után, vesszővel elválasztva.

Osztályok

Az osztályok objektumok tervrajzai, amelyekkel több azonos struktúrájú objektumot hozhatunk létre. TypeScript-ben az osztályok nagyon hasonlóan működnek, mint JavaScriptben, vagy Javaban.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Product {
    id: number;
    name: string;
    price: number;
    category: string;

    constructor(id: number, name: string, price: number, category: string) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.category = category;
    }

    getInfo(): string {
        return `${this.name} (${this.category}) - $${this.price}`;
    }

    applyDiscount(percentage: number): number {
        return this.price * (1 - percentage / 100);
    }
}

const laptop = new Product(1, "Laptop", 999.99, "Electronics");
const coffee = new Product(2, "Coffee", 10.99, "Food");

console.log(laptop.getInfo());           // "Laptop (Electronics) - $999.99"
console.log(laptop.applyDiscount(10));   // 899.991

Láthatósági módosítók (Access Modifiers)

A láthatósági módosítókkal szabályozhatjuk, hogy az osztály adattagjai és metódusai honnan érhetők el.

  • public - Bárhonnan elérhető (alapértelmezett)
  • private - Csak az osztályon belül elérhető
  • protected - Az osztályban és az örökölt osztályokban elérhető

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
class Product {
    public readonly id: number;
    public name: string;
    private basePrice: number;
    public category: string;

    constructor(id: number, name: string, basePrice: number, category: string) {
        this.id = id;
        this.name = name;
        this.basePrice = basePrice;
        this.category = category;
    }

    getPrice(): number {
        return this.basePrice;
    }

    applyDiscount(percentage: number): number {
        return this.basePrice * (1 - percentage / 100);
    }

    private calculateTax(): number {
        return this.basePrice * 0.27;
    }

    getPriceWithTax(): number {
        return this.basePrice + this.calculateTax();
    }
}

const laptop = new Product(1, "Laptop", 999.99, "Electronics");

console.log(laptop.name);              // OK - public
console.log(laptop.getPrice());        // OK - public method
// console.log(laptop.basePrice);      // Error - private
// console.log(laptop.calculateTax()); // Error - private method
A private módosító segít elrejteni az implementációs részleteket és megakadályozza az illetéktelen adathozzáféréseket. Így biztosíthatjuk, hogy az osztály állapota csak az általunk definiált metódusokon keresztül változzon.

Rövidített konstruktor szintaxis

TypeScript-ben a konstruktor paramétereit egyszerűbben is definiálhatjuk a láthatósági módosítók (public, private, protected) használatával. Ez automatikusan létrehozza és inicializálja a tulajdonságokat.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
class Product {

    constructor(
        public id: number,
        public name: string,
        public price: number,
        public category: string
    ) {}

}

Természetesen ennek használata nem kötelező, azonban előfordulat, hogy a valóságban ilyen kódot látunk, ilyenkor ne ijedjünk meg.

Getter és Setter

A (property) getter-ek és setter-ek segítségével hagyományos adattagokhoz hasonlóan érhetünk el metódusokat. Ez hasznos, amikor valamilyen logikát szeretnénk futtatni az érték lekérésekor vagy beállításakor. Továbbá, természetesen privát adattagokhoz biztosíthatunk ellenőrzött elérést és módosítást.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Product {
    public readonly id: number;
    public name: string;
    private basePrice: number;
    public category: string;

    constructor(id: number, name: string, basePrice: number, category: string) {
        this.id = id;
        this.name = name;
        this.basePrice = basePrice;
        this.category = category;
    }

    get price(): number {
        return this.basePrice * (1 - this.discountPercentage / 100);
    }

    set discount(percentage: number) {
        if (percentage >= 0 && percentage <= 100) {
            this.discountPercentage = percentage;
        } else {
            throw new Error("Discount must be between 0 and 100");
        }
    }

    get formattedPrice(): string {
        return `$${this.price.toFixed(2)}`;
    }
}

const laptop = new Product(1, "Laptop", 999.99, "Electronics");

console.log(laptop.price);           // 999.99 - using getter
laptop.discount = 10;                // using setter
console.log(laptop.price);           // 899.991 - discounted price
console.log(laptop.formattedPrice);  // "$899.99"

// laptop.discount = 150;            // Error: Discount must be between 0 and 100

Static tulajdonságok és metódusok

A statikus tulajdonságok és metódusok az osztályhoz tartoznak, nem az egyes példányokhoz. Közvetlenül az osztály nevén keresztül érhetőek el.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class Product {
    public readonly id: number;
    public name: string;
    private basePrice: number;
    public category: string;


    private static nextId: number = 1;

    constructor(id: number, name: string, basePrice: number, category: string) {
        this.id = id;
        this.name = name;
        this.basePrice = basePrice;
        this.category = category;
    }

    // Static method to generate unique IDs
    static generateId(): number {
        return Product.nextId++;
    }

    // Static method to create products with auto-generated ID
    static create(name: string, price: number, category: string): Product {
        return new Product(Product.generateId(), name, price, category);
    }
}

const laptop = Product.create("Laptop", 999.99, "Electronics");
const mouse = Product.create("Mouse", 29.99, "Electronics");
const coffee = Product.create("Coffee", 10.99, "Food");

console.log(laptop.id);  // 1
console.log(mouse.id);   // 2
console.log(coffee.id);  // 3

Interface implementálása osztályban

Az osztályok implementálhatnak interface-eket (természetesen), ami biztosítja, hogy az osztály rendelkezik az interface által megkövetelt tulajdonságokkal és metódusokkal.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
interface IProduct {
    id: number;
    name: string;
    price: number;
    category: string;
    getInfo(): string;
    applyDiscount(percentage: number): number;
}

class Product implements IProduct {
    constructor(
        public id: number,
        public name: string,
        public price: number,
        public category: string
    ) {}

    getInfo(): string {
        return `${this.name} (${this.category}) - $${this.price}`;
    }

    applyDiscount(percentage: number): number {
        return this.price * (1 - percentage / 100);
    }
}

const laptop: IProduct = new Product(1, "Laptop", 999.99, "Electronics");

Öröklődés (Inheritance)

Az öröklődés lehetővé teszi, hogy egy osztály (leszármazott/gyermek/child) örökölje egy másik osztály (ős/szülő/parent) tulajdonságait és metódusait. Az extends kulcsszóval jelöljük itt is.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Product {
    public id: number;
    public name: string;
    public price: number;
    public category: string;

    constructor(id: number, name: string, price: number, category: string) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.category = category;
    }

    getInfo(): string {
        return `${this.name} (${this.category}) - $${this.price}`;
    }

    applyDiscount(percentage: number): number {
        return this.price * (1 - percentage / 100);
    }
}

class Electronics extends Product {
    public brand: string;
    public warrantyMonths: number;

    constructor(id: number, name: string, price: number, brand: string, warrantyMonths: number) {
        super(id, name, price, "Electronics");
        this.brand = brand;
        this.warrantyMonths = warrantyMonths;
    }

    getWarrantyInfo(): string {
        return `${this.warrantyMonths} months warranty`;
    }

    getInfo(): string {
        return `${super.getInfo()} - Brand: ${this.brand}, ${this.getWarrantyInfo()}`;
    }
}

const laptop = new Electronics(1, "Laptop", 999.99, "Dell", 24);

console.log(laptop.getInfo());
// "Laptop (Electronics) - $999.99 - Brand: Dell, 24 months warranty"

console.log(laptop.applyDiscount(10));  // 899.991 - inherited method
console.log(laptop.getWarrantyInfo());  // "24 months warranty" - new method

A super kulcsszó

  • A konstruktorban a super() meghívja a szülő osztály konstruktorát
  • Metódusokban a super.methodName() meghívja a szülő metódusát
  • A super() konstruktor hívást mindig meg kell tennünk a gyermek konstruktorában, még a saját adattagok inicializálása előtt.

Protected láthatóság

A protected módosító lehetővé teszi, hogy a szülő osztály adattagjai és metódusai elérhetők legyenek a gyermek osztályokban is.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
class Product {
    public id: number;
    public name: string;
    protected basePrice: number;
    public category: string;

    constructor(id: number, name: string, basePrice: number, category: string) {
        this.id = id;
        this.name = name;
        this.basePrice = basePrice;
        this.category = category;
    }

    getPrice(): number {
        return this.basePrice;
    }

    protected calculateDiscount(percentage: number): number {
        return this.basePrice * (percentage / 100);
    }
}

class Electronics extends Product {
    public brand: string;

    constructor(id: number, name: string, price: number, brand: string) {
        super(id, name, price, "Electronics");
        this.brand = brand;
    }

    applySeasonalDiscount(): number {
        const discount = this.calculateDiscount(15);
        return this.basePrice - discount;
    }
}

const laptop = new Electronics(1, "Laptop", 999.99, "Dell");
console.log(laptop.applySeasonalDiscount());     // 849.9915
// console.log(laptop.basePrice);                // Error - protected
// console.log(laptop.calculateDiscount(10));    // Error - protected

Absztrakt osztályok

Az absztrakt osztályok nem példányosíthatóak közvetlenül, csak arra szolgálnak, hogy más osztályok örököljenek belőlük. Tartalmazhatnak absztrakt metódusokat, amelyeket a gyermek osztályoknak kötelező implementálni (különben azok is absztraktok lesznek).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
abstract class Product {
    public id: number;
    public name: string;
    protected basePrice: number;
    public category: string;

    constructor(id: number, name: string, basePrice: number, category: string) {
        this.id = id;
        this.name = name;
        this.basePrice = basePrice;
        this.category = category;
    }

    abstract getDetails(): string;

    abstract calculateShipping(): number;

    getTotalCost(): number {
        return this.basePrice + this.calculateShipping();
    }
}

class Food extends Product {
    constructor(id: number, name: string, price: number, public expiryDate: string) {
        super(id, name, price, "Food");
    }

    getDetails(): string {
        return `Expires: ${this.expiryDate}`;
    }

    calculateShipping(): number {
        return 3;
    }
}

const coffee = new Food(2, "Coffee", 10.99, '2025-12-31');
console.log(coffee.getDetails());
console.log(coffee.getTotalCost());

Feladat

Spotify lejátszási lista

Egy egyszerű zenelejátszó rendszert kell készítened, ahol zeneszámokat tudsz kezelni és lejátszási listákba rendezni. Hozz létre egy Track nevű interface-t (vagy type aliast) a következő tulajdonságokkal:

  • A szám címe szövegként
  • Az előadó neve szövegként
  • A szám hossza másodpercben egész számként

Készíts egy Playlist osztályt az alábbi követelmények szerint:

Adattagok: - Privát adattag a lejátszási lista nevének tárolására - Privát adattag a hozzáadott számok tömbjének tárolására

Konstruktor: - Paraméterként kapja meg a lista nevét - A számok tömbje kezdetben üres

Getterek és setterek: - Getter a névhez - Getter a számok tömbjéhez (ez másolatot adjon vissza) - Setter a névhez, ami ellenőrzi, hogy a név nem üres string (és legalább 10 karakter, de nem több, mint 64).

Metódusok: - Metódus szám hozzáadásához (egy Track objektumot kap paraméterként) - Metódus szám eltávolításához cím alapján (ez az első találatot törölje, ha több azonos című szám lenne) - Getter, ami kiszámítja és visszaadja a teljes lejátszási időt másodpercekben - Getter, ami kiszámítja és visszaadja a teljes lejátszási időt percekben - Metódus, ami visszaadja a leghosszabb számot.

Food Delivery alkalmazás

Egy ételrendelő alkalmazás alapjait kell megvalósítanod, ahol különböző típusú ételeket lehet kezelni és rendelni. Hozz létre egy Orderable interface-t:

  • Egy getFinalPrice() metódust ír elő, ami számot ad vissza
  • Ez a végleges árat adja meg, ami tartalmazhatja az extra feltétek árát is

Készíts egy absztrakt MenuItem osztályt:

Adattagok (protected): - Az étel neve (szöveg) - Az étel alapára (szám) - Az étel kalóriatartalma (szám)

Konstruktor: - Inicializálja mindhárom értéket a kapott paraméterek alapján

Getterek és setterek: - Getter mindhárom tulajdonsághoz - Setter az árhoz, ami ellenőrzi, hogy az ár pozitív szám

Absztrakt metódus: - getDescription() - szöveges leírást ad vissza az ételről (a leszármazott osztályok valósítják meg)

A Pizza osztály örököljön a MenuItem osztályból és valósítsa meg az Orderable interface-t.

Adattagok: - Privát logikai adattag, ami jelzi, hogy kér-e extra sajtot a vásárló (alapértelmezetten false)

Getterek és setterek: - Getter és setter az extra sajt opcióhoz

Metódusok: - getDescription() - visszaadja a pizza leírását (pl. "Margherita pizza extra sajttal" vagy "Margherita pizza") - getFinalPrice() - visszaadja az alapárat, plusz 500 Ft-ot ha van extra sajt

A Burger osztály szintén örököljön a MenuItem osztályból és valósítsa meg az Orderable interface-t.

Adattagok: - Privát adattag a húspogácsa méretéhez (lehet: "kicsi", "közepes", "nagy")

Getterek és setterek: - Getter és setter a mérethez

Metódusok: - getDescription() - visszaadja a burger leírását a mérettel együtt (pl. "Nagy dupla sajtos burger") - getFinalPrice() - az alapárhoz adjon hozzá: - +200 Ft közepes méret esetén - +400 Ft nagy méret esetén - +0 Ft kicsi méret esetén

Webshop kosár

Készítsünk egy webshop kosár rendszert az alábbi követelményekkel:

  1. CartItem interface - Kosár tételek szerkezete
  2. ShoppingCart osztály - Kosár kezelése
  3. Discount abstract osztály - Kedvezmények kezelése
  4. Konkrét kedvezmény típusok - Percentage és Fixed discount

1. CartItem interface

1
2
3
4
interface CartItem {
    product: Product;
    quantity: number;
}

2. ShoppingCart osztály

Implementáljuk a ShoppingCart osztályt az alábbi követelményekkel:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
class ShoppingCart {
    private items: CartItem[] = [];

    // Add product to cart
    addItem(product: Product, quantity: number = 1): void {
        // TODO: Check if product already in cart
        // If yes, increase quantity
        // If no, add new item
    }

    // Remove product from cart
    removeItem(productId: number): void {
        // TODO: Remove item by product id
    }

    // Update quantity
    updateQuantity(productId: number, quantity: number): void {
        // TODO: Update quantity for a product
        // If quantity is 0, remove item
    }

    // Get total price
    getTotal(): number {
        // TODO: Calculate total price of all items
    }

    // Get item count
    get itemCount(): number {
        // TODO: Return total number of items (sum of quantities)
    }

    // Get all items
    getItems(): CartItem[] {
        // TODO: Return copy of items array
    }

    // Clear cart
    clear(): void {
        // TODO: Empty the cart
    }

    // Display cart contents
    displayCart(): void {
        // TODO: Print cart contents to console
    }
}

3. Discount abstract osztály

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
abstract class Discount {
    constructor(public name: string) {}

    // Abstract method - calculate discount amount
    abstract calculate(total: number): number;

    // Apply discount to total
    apply(total: number): number {
        const discountAmount = this.calculate(total);
        return Math.max(0, total - discountAmount);
    }

    // Get discount description
    abstract getDescription(): string;
}

4. Konkrét kedvezmény típusok

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class PercentageDiscount extends Discount {
    constructor(name: string, private percentage: number) {
        super(name);
    }

    calculate(total: number): number {
        // TODO: Calculate percentage discount
    }

    getDescription(): string {
        // TODO: Return description like "10% off"
    }
}

class FixedDiscount extends Discount {
    constructor(name: string, private amount: number) {
        super(name);
    }

    calculate(total: number): number {
        // TODO: Return fixed discount amount
    }

    getDescription(): string {
        // TODO: Return description like "$20 off"
    }
}

Használat (teszt kód)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// Create products
const laptop = new Electronics(1, "Laptop", 999.99, "Dell", 2.5);
const mouse = new Electronics(2, "Mouse", 29.99, "Logitech", 0.2);
const coffee = new Food(3, "Coffee", 10.99, new Date('2025-12-31'));

// Create cart
const cart = new ShoppingCart();

// Add items
cart.addItem(laptop, 1);
cart.addItem(mouse, 2);
cart.addItem(coffee, 3);

// Display cart
cart.displayCart();
console.log(`Total: $${cart.getTotal().toFixed(2)}`);
console.log(`Items in cart: ${cart.itemCount}`);

// Apply discounts
const percentDiscount = new PercentageDiscount("Holiday Sale", 10);
const fixedDiscount = new FixedDiscount("Welcome Bonus", 20);

console.log(`\nWith ${percentDiscount.getDescription()}: $${percentDiscount.apply(cart.getTotal()).toFixed(2)}`);
console.log(`With ${fixedDiscount.getDescription()}: $${fixedDiscount.apply(cart.getTotal()).toFixed(2)}`);

// Update quantity
cart.updateQuantity(2, 1); // Change mouse quantity to 1
cart.displayCart();

// Remove item
cart.removeItem(3); // Remove coffee
cart.displayCart();

Elvárt kimenet

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
=== Shopping Cart ===
1x Laptop (Electronics) - $999.99
2x Mouse (Electronics) - $59.98
3x Coffee (Food) - $32.97
Total: $1092.94
======================

Items in cart: 6

With 10% off: $983.65
With $20 off: $1072.94

=== Shopping Cart ===
1x Laptop (Electronics) - $999.99
1x Mouse (Electronics) - $29.99
Total: $1029.98
======================

=== Shopping Cart ===
1x Laptop (Electronics) - $999.99
1x Mouse (Electronics) - $29.99
Total: $1029.98
======================