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.
| 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:
- CartItem interface - Kosár tételek szerkezete
- ShoppingCart osztály - Kosár kezelése
- Discount abstract osztály - Kedvezmények kezelése
- Konkrét kedvezmény típusok - Percentage és Fixed discount
1. CartItem interface
| interface CartItem {
product: Product;
quantity: number;
}
|
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
======================
|