third week

This commit is contained in:
Donát committed 2026-09-08 09:36:02 +02:00
1 parent 95e62c915f
commit a935851e22
31 files changed
+1269 -33

No files matched your search

@@ -0,0 +1,92 @@
\section{Bevezetés}
\begin{frame}{Miről szól ez az anyag?}
\footnotesize
Ez egy \textbf{önállóan is végigkövethető, kiegészítő} tananyag a TypeScriptről. Nem feltételezi, hogy láttad a 2. hét TypeScript diáit -- a legelejétől indulunk, majd jóval mélyebbre megyünk, mint amit egy heti óra kerete engedne.
\begin{itemize}
\item Ha még sosem láttál TypeScript kódot, nyugodtan kezdd itt
\item Ha a 2. héten már megismerted az alapokat, néhány dia ismétlés lesz -- utána viszont új anyag következik
\item A cél: hogy magabiztosan tudj TypeScript kódot olvasni és írni, ne csak felismerni
\end{itemize}
\begin{alertblock}{Nem kötelező anyag}
Ez a diasor \emph{nem} számonkérendő tananyag -- annak szól, aki jobban meg szeretné érteni a TypeScript-et, mint amire a heti órák ideje engedne.
\end{alertblock}
\end{frame}
\begin{frame}{Mi az a TypeScript, és miért érdemes használni?}
\footnotesize
A JavaScript \textbf{gyengén típusos} nyelv: egy változó bármikor bármilyen típusú értéket felvehet, és a legtöbb hiba csak futás közben derül ki. A \textbf{TypeScript} (Microsoft, 2012) a JavaScript egy kiterjesztése, ami \emph{statikus típusrendszert} ad hozzá -- a kód végül mindig sima JavaScript-té fordul le.
\begin{itemize}
\item A típushibák nagy része már íráskor / fordításkor kiderül, nem futás közben, esetleg egy élő felhasználónál
\item Az IDE (VS Code, stb.) pontos kódkiegészítést, azonnali hibajelzést tud adni
\item A kód önmagát dokumentálja: egy függvény szignatúrájából látszik, mit vár és mit ad vissza
\item Nagyobb csapatban, nagyobb, hosszú távon karbantartott kódbázisban különösen megtérül
\end{itemize}
\begin{block}{Fontos}
A TypeScript \emph{opcionális, fokozatosan bevezethető} réteg -- egy meglévő JavaScript projektbe fájlonként is átállhatunk rá.
\end{block}
\end{frame}
\begin{frame}[fragile]{Telepítés és a \texttt{tsconfig.json}}
\footnotesize
A TypeScript fordítót (\texttt{tsc}) npm csomagként telepítjük, majd egy konfigurációs fájlt hozunk létre, ami megmondja a fordítónak, hogyan viselkedjen.
\begin{block}{Telepítés}
\begin{verbatim}
npm install -D typescript
npx tsc --init
\end{verbatim}
\end{block}
\begin{block}{A \texttt{tsconfig.json} legfontosabb mezői}
\begin{itemize}
\item \texttt{target} -- milyen JavaScript verzióra fordítson (pl. \texttt{"ES2020"})
\item \texttt{module} -- modulrendszer (\texttt{"commonjs"} vagy \texttt{"esnext"})
\item \texttt{outDir} / \texttt{rootDir} -- hova kerüljön a lefordított kód, honnan olvasson forrást
\item \texttt{strict} -- bekapcsolja az összes szigorú típusellenőrzést (\emph{erősen ajánlott} \texttt{true})
\end{itemize}
\end{block}
\end{frame}
\begin{frame}[fragile]{Futtatás fejlesztés közben}
\footnotesize
A \texttt{.ts} fájlokat sem a böngésző, sem a Node.js nem futtatja közvetlenül -- előbb JavaScript-té kell fordítani. Fejlesztés közben ezt kényelmesebb eszközökkel oldjuk meg, mint kézzel fordítani minden mentés után.
\begin{block}{Klasszikus fordítás}
\begin{verbatim}
npx tsc # egyszeri forditas
npx tsc --watch # automatikusan ujrafordit mentes utan
\end{verbatim}
\end{block}
\begin{block}{Közvetlen futtatás (nincs külön fordítási lépés)}
\begin{verbatim}
npx tsx index.ts
\end{verbatim}
A \texttt{tsx} (és a régebbi \texttt{ts-node}) memóriában fordítja és rögtön futtatja is a kódot -- ez a legkényelmesebb fejlesztés közben.
\end{block}
\end{frame}
\begin{frame}[fragile]{Az első \texttt{.ts} fájl}
\footnotesize
Nézzük meg, hogyan néz ki egy egyszerű, típusozott TypeScript fájl -- ez lesz az alapja a következő diáknak, ahol részletesen végigvesszük az egyes típusokat.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
function add(a: number, b: number): number {
return a + b;
}
const result = add(2, 3);
console.log(`Az eredmeny: ${result}`);
// add('2', 3); -- ez forditasi hibat adna!
\end{lstlisting}
\begin{block}{Amit érdemes megfigyelni}
A paraméterek és a visszatérési érték típusát kettősponttal adjuk meg. Ha rossz típusú értékkel próbálnánk hívni a függvényt, a fordító \emph{azonnal} hibát jelezne -- még mielőtt a kód lefutna.
\end{block}
\end{frame}
@@ -0,0 +1,99 @@
\section{Alaptípusok}
\begin{frame}[fragile]{Primitív típusok}
\footnotesize
A TypeScript primitív típusai megegyeznek a JavaScript primitív értékeivel -- csak most a fordító is számon tartja, melyik változó melyik típusú.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
let name: string = 'Anna';
let age: number = 25;
let isActive: boolean = true;
let id: bigint = 123n;
let nothing: null = null;
let notSet: undefined = undefined;
\end{lstlisting}
\begin{itemize}
\item \texttt{string}, \texttt{number}, \texttt{boolean} -- a leggyakrabban használt három típus
\item \texttt{null} és \texttt{undefined} önálló típusként is léteznek, de gyakrabban más típusokkal együtt (pl. \texttt{string | null}) fordulnak elő
\item \texttt{bigint} és \texttt{symbol} ritkábban használt, speciális típusok
\end{itemize}
\end{frame}
\begin{frame}[fragile]{Tömbök és tuple-ök}
\footnotesize
A tömböknél meg kell adni, milyen típusú elemeket tartalmaznak. A \textbf{tuple} egy speciális, rögzített hosszúságú és típussorrendű tömb.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
let names: string[] = ['Anna', 'Bela'];
let scores: Array<number> = [10, 20, 30];
// Tuple: pontosan ket elem, elore rogzitett tipussal
let point: [number, number] = [3, 5];
let entry: [string, number] = ['age', 25];
\end{lstlisting}
\begin{block}{Miért hasznos a tuple?}
Egy \texttt{[string, number]} tuple azonnal jelzi, hogy pontosan két elemet várunk, meghatározott sorrendben -- ellentétben egy sima \texttt{(string | number)[]} tömbbel, ami bármilyen hosszú és sorrendű keveréket engedne.
\end{block}
\end{frame}
\begin{frame}[fragile]{\texttt{any}, \texttt{unknown} és \texttt{never}}
\footnotesize
Három speciális típus, amit érdemes megkülönböztetni -- könnyű összekeverni őket, de nagyon más a szerepük.
\begin{itemize}
\item \textbf{\texttt{any}:} teljesen kikapcsolja a típusellenőrzést erre a változóra -- gyakorlatilag "visszatérünk sima JavaScript-hez". Kerülendő, csak végső esetben használjuk!
\item \textbf{\texttt{unknown}:} szintén "bármi lehet", de \emph{használat előtt} típusellenőrzést (narrowing) kényszerít ki -- ez a biztonságos alternatívája az \texttt{any}-nek
\item \textbf{\texttt{never}:} olyan érték típusa, ami \emph{sosem} fordulhat elő -- pl. egy függvény, ami mindig hibát dob, vagy egy végtelen ciklus visszatérési típusa
\end{itemize}
\begin{alertblock}{Ökölszabály}
Ha típust nem tudsz vagy nem akarsz megadni, használj \texttt{unknown}-t \texttt{any} helyett -- így a fordító legalább kikényszeríti, hogy ellenőrizd az értéket, mielőtt használnád.
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Type inference -- amikor nem kell kiírni a típust}
\footnotesize
A TypeScript sok esetben \emph{magától kikövetkezteti} (infer) a típust a kezdőértékből -- nem kell mindenhol explicit típust írnunk.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
let name = 'Anna'; // TypeScript tudja: ez string
let age = 25; // TypeScript tudja: ez number
name = 42; // Hiba! nem lehet number-t string-nek adni
\end{lstlisting}
\begin{block}{Mikor írjunk mégis explicit típust?}
Függvényparamétereknél szinte mindig érdemes (a fordító nem tudja kitalálni, mit fogunk hívni), de egyszerű, azonnal értékadott lokális változóknál nyugodtan hagyatkozhatunk az inferálásra.
\end{block}
\end{frame}
\begin{frame}[fragile]{Type alias -- saját név egy típusnak}
\footnotesize
A \texttt{type} kulcsszóval nevet adhatunk egy típusnak -- ez különösen hasznos, ha egy összetettebb típust (pl. union típust, amit a 4. fejezetben veszünk) többször is használunk.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
type UserId = number;
type Username = string;
function getUser(id: UserId): Username {
return 'Anna';
}
\end{lstlisting}
Ez még nem sokkal több, mint egy elnevezés -- de amint a következő fejezetekben összetettebb típusokat (union, interfész) ismerünk meg, a \texttt{type} igazán hasznossá válik.
\end{frame}
\begin{frame}{Összefoglalás -- Alaptípusok}
\footnotesize
\begin{block}{Alaptípusok}
\begin{itemize}
\item Primitívek: \texttt{string}, \texttt{number}, \texttt{boolean}, \texttt{null}, \texttt{undefined}, \texttt{bigint}
\item Tömbök: \texttt{string[]}; Tuple: rögzített hosszú/sorrendű tömb, pl. \texttt{[string, number]}
\item \texttt{any} (kerülendő) vs \texttt{unknown} (biztonságos) vs \texttt{never} (sosem előforduló érték)
\item Type inference: a fordító sokszor magától kitalálja a típust
\item \texttt{type} -- nevet adhatunk egy típusnak, újrafelhasználhatóvá téve azt
\end{itemize}
\end{block}
\end{frame}
@@ -0,0 +1,90 @@
\section{Interface/Type}
\begin{frame}[fragile]{Interfész alapok}
\footnotesize
Az \texttt{interface} egy objektum \emph{alakját} (mely mezők, milyen típussal) írja le. Ahol egy interfészt típusként megadunk, a fordító ellenőrzi, hogy az érték tényleg megfelel-e neki.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
interface User {
id: number;
name: string;
email: string;
nickname?: string; // opcionalis mezo
readonly createdAt: Date; // csak olvashato
}
const user: User = {
id: 1,
name: 'Anna',
email: 'anna@example.com',
createdAt: new Date(),
};
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]{Opcionális és csak olvasható mezők}
\footnotesize
Két apró, de nagyon hasznos jelölés van, amivel pontosabbá tehetjük az interfészünket: a \texttt{?} és a \texttt{readonly}.
\begin{itemize}
\item \textbf{\texttt{mezo?: tipus}} -- a mező elhagyható, de ha megadjuk, a megjelölt típusúnak kell lennie
\item \textbf{\texttt{readonly mezo: tipus}} -- a mező az objektum létrehozása után nem módosítható
\end{itemize}
\begin{lstlisting}[language=JavaScript,style=NodeJS]
const user: User = { id: 1, name: 'Anna',
email: 'a@b.com', createdAt: new Date() };
user.createdAt = new Date(); // Hiba! readonly mezo
\end{lstlisting}
\begin{block}{Miért hasznos?}
A \texttt{readonly} segít elkerülni azokat a hibákat, amikor véletlenül módosítunk egy olyan mezőt, aminek soha nem szabadna változnia (pl. egy azonosítót).
\end{block}
\end{frame}
\begin{frame}[fragile]{Interfészek kiterjesztése}
\footnotesize
Egy interfész \texttt{extends}-szel örökölheti egy másik interfész mezőit -- így elkerülhetjük az ismétlődést hasonló, egymásra épülő alakoknál.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
interface Animal {
name: string;
age: number;
}
interface Dog extends Animal {
breed: string;
}
const myDog: Dog = { name: 'Buksi', age: 3, breed: 'labrador' };
\end{lstlisting}
A \texttt{Dog} interfész mindent örököl az \texttt{Animal}-tól, és hozzáad egy \texttt{breed} mezőt -- pontosan úgy, ahogy egy osztály is örökölhet egy másiktól.
\end{frame}
\begin{frame}{\texttt{interface} vs \texttt{type} -- mikor melyiket?}
\footnotesize
A két eszköz sokban hasonlít, és a legtöbb egyszerű esetben szinte mindegy, melyiket választjuk -- de van néhány különbség, ami eldöntheti a választást.
\begin{itemize}
\item \textbf{\texttt{interface}:} csak objektum alakok leírására való; több, azonos nevű interfész deklaráció automatikusan összeolvad (declaration merging); \texttt{extends}-szel bővíthető
\item \textbf{\texttt{type}:} bármilyen típust elnevezhet -- objektumot, uniót, primitívet is; nem olvad össze automatikusan, de intersection-nel (\texttt{\&}) kombinálható
\end{itemize}
\begin{block}{Gyakorlati ökölszabály}
Objektum \emph{alakokhoz} (pl. egy adatmodell) válaszd az \texttt{interface}-t -- olvashatóbb hibaüzeneteket ad. Union típusokhoz (a következő fejezet) csak a \texttt{type} jöhet szóba.
\end{block}
\end{frame}
\begin{frame}{Összefoglalás -- Interface és Type}
\footnotesize
\begin{block}{Interface és Type}
\begin{itemize}
\item \texttt{interface}: objektum alakjának leírása -- mezők, opcionális (\texttt{?}) és readonly mezők
\item \texttt{extends}: interfészek egymásra építése, kódismétlés elkerülése
\item \texttt{type}: bármilyen típusnak adhatunk nevet, nemcsak objektumoknak
\item Objektum alakoknál \texttt{interface}, union típusoknál \texttt{type} -- ökölszabályként
\end{itemize}
\end{block}
\end{frame}
@@ -0,0 +1,88 @@
\section{Függvények}
\begin{frame}[fragile]{Függvények típusozása}
\footnotesize
Egy TypeScript függvénynél mind a paraméterek, mind a visszatérési érték típusát megadhatjuk -- a visszatérési típust a fordító a legtöbbször ki is következteti, de explicit kiírva olvashatóbb, és véd az elgépelés ellen.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
function multiply(a: number, b: number): number {
return a * b;
}
// Nincs visszatero ertek -> void
function logMessage(message: string): void {
console.log(message);
}
\end{lstlisting}
\begin{block}{Miért érdemes kiírni a visszatérési típust?}
Ha véletlenül rossz típusú értéket adnánk vissza (pl. \texttt{string}-et \texttt{number} helyett), a fordító azonnal jelezné a hibát -- még mielőtt bárhol meghívnánk a függvényt.
\end{block}
\end{frame}
\begin{frame}[fragile]{Opcionális és alapértelmezett paraméterek}
\footnotesize
Nem minden paraméter kötelező -- a JavaScript-hez hasonlóan itt is megadhatunk alapértelmezett értéket, vagy jelölhetjük egy paramétert opcionálisként.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
function greet(name: string, greeting: string = 'Szia'): string {
return `${greeting}, ${name}!`;
}
function createUser(name: string, nickname?: string) {
return { name, nickname: nickname ?? name };
}
greet('Anna'); // "Szia, Anna!"
greet('Anna', 'Hello'); // "Hello, Anna!"
\end{lstlisting}
\begin{alertblock}{Fontos sorrend}
Az opcionális (\texttt{?}) és alapértelmezett paraméterek csak a kötelező paraméterek \emph{után} szerepelhetnek a paraméterlistában.
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Függvénytípusok -- callback-ek típusozása}
\footnotesize
Amikor egy függvény egy másik függvényt vár paraméterként (pl. egy callback-et), magának a "függvény alaknak" is megadhatjuk a típusát.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
type MathOperation = (a: number, b: number) => number;
function calculate(a: number, b: number, op: MathOperation): number {
return op(a, b);
}
const sum: MathOperation = (a, b) => a + b;
calculate(2, 3, sum); // 5
\end{lstlisting}
Ez különösen hasznos Express.js-ben: egy route handler is pontosan egy ilyen "függvény alakú" típus (\texttt{(req, res) => void}).
\end{frame}
\begin{frame}{Function overloads -- röviden}
\footnotesize
Néha egy függvény többféle paraméter-kombinációval is hívható, és a visszatérési típus a bemenettől függ. Erre valók a \textbf{overloads}: több szignatúra deklarálása egyetlen implementáció előtt.
\begin{itemize}
\item Több "fejlécet" (szignatúrát) írunk le, majd egyetlen tényleges implementációt
\item A hívó fél oldaláról úgy tűnik, mintha több, különböző típusú változata lenne a függvénynek
\item Ritkán szükséges kezdőként -- inkább unió típusokkal (következő fejezet) old meg a legtöbb esetet
\end{itemize}
\begin{block}{Gyakorlati tanács}
Ha csak most ismerkedsz a TypeScript-tel, nem kell aktívan használnod az overloads-ot -- elég felismerni, ha egy könyvtár dokumentációjában találkozol vele.
\end{block}
\end{frame}
\begin{frame}{Összefoglalás -- Függvények}
\footnotesize
\begin{block}{Függvények TypeScript-ben}
\begin{itemize}
\item Paraméterek és visszatérési típus típusozása -- \texttt{void}, ha nincs visszatérő érték
\item Alapértelmezett érték (\texttt{= ...}) és opcionális paraméter (\texttt{?}), mindig a kötelezők után
\item Függvénytípusok: \texttt{(a: number, b: number) => number} -- callback-ek típusozásához
\item Overloads: több szignatúra egy implementációhoz -- ritkán szükséges kezdőként
\end{itemize}
\end{block}
\end{frame}
@@ -0,0 +1,90 @@
\section{Union/Intersection}
\begin{frame}[fragile]{Union típusok -- "vagy ez, vagy az"}
\footnotesize
Egy \textbf{union} típus (\texttt{|} jellel) azt fejezi ki, hogy egy érték \emph{többféle} típus egyike lehet. Ez az egyik legtöbbet használt eszköz a valós TypeScript kódban.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
type Id = string | number;
function printId(id: Id) {
console.log(`Az azonosito: ${id}`);
}
printId(123); // OK
printId('abc-123'); // OK is
\end{lstlisting}
\begin{block}{Gyakori példa}
\texttt{type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';} -- ilyenkor a fordító nem enged elgépelt, érvénytelen metódusnevet.
\end{block}
\end{frame}
\begin{frame}[fragile]{Type narrowing -- a típus leszűkítése}
\footnotesize
Ha egy változó union típusú, a fordító csak azokat a műveleteket engedi rajta, amik \emph{minden} lehetséges típusnál érvényesek -- amíg nem szűkítjük le (narrowing), melyik konkrét típusról van szó.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
function printId(id: string | number) {
if (typeof id === 'string') {
console.log(id.toUpperCase()); // itt mar biztos string
} else {
console.log(id.toFixed(2)); // itt mar biztos number
}
}
\end{lstlisting}
\begin{itemize}
\item \texttt{typeof} -- primitív típusok megkülönböztetésére (\texttt{string}, \texttt{number}, stb.)
\item \texttt{instanceof} -- osztálypéldányok megkülönböztetésére
\item \texttt{in} -- egy adott mező meglétének ellenőrzésére egy objektumon
\end{itemize}
\end{frame}
\begin{frame}[fragile]{Intersection típusok -- "ez is, az is"}
\footnotesize
Az \textbf{intersection} típus (\texttt{\&} jellel) ennek épp az ellentéte: egy érték \emph{egyszerre} feleljen meg több típusnak is -- a mezők összeadódnak.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
interface HasId {
id: number;
}
interface HasName {
name: string;
}
type Entity = HasId & HasName;
const user: Entity = { id: 1, name: 'Anna' };
\end{lstlisting}
Az \texttt{Entity} típusú értéknek \emph{mindkét} interfész mezőit tartalmaznia kell -- ez hasznos, ha kisebb, újrafelhasználható "építőelemekből" rakunk össze nagyobb típusokat.
\end{frame}
\begin{frame}[fragile]{Literal típusok}
\footnotesize
A TypeScript-ben nemcsak \texttt{string} vagy \texttt{number} lehet egy típus, hanem egy \emph{konkrét érték} is -- ezt hívjuk literal típusnak. Union-nal kombinálva rendkívül kifejező, önmagát dokumentáló kódot kapunk.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
type Status = 'pending' | 'active' | 'closed';
function setStatus(status: Status) {
// csak a harom felsorolt string-et fogadja el
}
setStatus('active'); // OK
setStatus('done'); // Hiba! nincs ilyen Status
\end{lstlisting}
\end{frame}
\begin{frame}{Összefoglalás -- Union és Intersection}
\footnotesize
\begin{block}{Union és Intersection típusok}
\begin{itemize}
\item Union (\texttt{|}): "vagy ez, vagy az" -- egy érték többféle típus egyike lehet
\item Type narrowing: \texttt{typeof}/\texttt{instanceof}/\texttt{in} -- leszűkítjük, melyik konkrét típusról van szó
\item Intersection (\texttt{\&}): "ez is, az is" -- több típus mezői összeadódnak
\item Literal típusok: konkrét értékek típusként, gyakran union-nal kombinálva (pl. HTTP metódusok)
\end{itemize}
\end{block}
\end{frame}
@@ -0,0 +1,89 @@
\section{Generikusok}
\begin{frame}[fragile]{A probléma, amit a generikák oldanak meg}
\footnotesize
Képzeljünk el egy függvényt, ami egy tömb első elemét adja vissza -- de szeretnénk, hogy \emph{bármilyen} típusú tömbre működjön, miközben megőrizzük a típusinformációt.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
function firstOfNumbers(arr: number[]): number {
return arr[0];
}
function firstOfStrings(arr: string[]): string {
return arr[0];
}
// ...es igy tovabb minden tipusra kulon fuggveny?
\end{lstlisting}
\begin{alertblock}{A probléma}
\texttt{any}-jal megoldhatnánk, de akkor elveszítenénk a típusbiztonságot: a fordító többé nem tudná, mit ad vissza a függvény.
\end{alertblock}
\end{frame}
\begin{frame}[fragile]{Generikus függvények}
\footnotesize
A \textbf{generikus típus} egy "típusparamétert" (jellemzően \texttt{T}) vezet be, amit a híváskor a konkrét típus tölt ki -- így egyetlen függvény működik bármilyen típusra, típusbiztonság elvesztése nélkül.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
function first<T>(arr: T[]): T {
return arr[0];
}
const n = first([1, 2, 3]); // T = number
const s = first(['a', 'b', 'c']); // T = string
\end{lstlisting}
A TypeScript minden híváskor kikövetkezteti, mi legyen \texttt{T} -- a visszatérési érték típusa mindig pontosan illeszkedik a bemenethez.
\end{frame}
\begin{frame}[fragile]{Generikus interfészek}
\footnotesize
Nemcsak függvények, hanem interfészek (és típusok) is lehetnek generikusak -- ez az alapja például annak, ahogy a legtöbb adatbázis-eredmény vagy API válasz típus felépül.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
interface Box<T> {
value: T;
}
const numberBox: Box<number> = { value: 42 };
const stringBox: Box<string> = { value: 'hello' };
interface ApiResponse<T> {
data: T;
success: boolean;
}
\end{lstlisting}
Egy \texttt{ApiResponse<User>} pontosan jelzi, hogy a \texttt{data} mező egy \texttt{User} objektumot fog tartalmazni -- ezt fogjuk a későbbi heteken az Express válaszainknál is használni.
\end{frame}
\begin{frame}[fragile]{Constraints -- a generikus típus korlátozása}
\footnotesize
Néha nem szeretnénk \emph{bármilyen} típust elfogadni -- csak olyanokat, amiknek van egy adott mezőjük vagy tulajdonságuk. Erre valók a \texttt{extends} kulcsszóval megadott \textbf{constraint}-ek.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(item: T): T {
console.log(item.length);
return item;
}
logLength('hello'); // OK - a string-nek van length-je
logLength([1, 2, 3]); // OK - a tombnek is
logLength(42); // Hiba! a number-nek nincs length-je
\end{lstlisting}
\end{frame}
\begin{frame}{Összefoglalás -- Generikus típusok}
\footnotesize
\begin{block}{Generikus típusok}
\begin{itemize}
\item A generikák elkerülik a kódduplikációt anélkül, hogy fel kellene adnunk a típusbiztonságot
\item \texttt{function first<T>(arr: T[]): T} -- a típusparamétert a hívás tölti ki
\item Interfészek/típusok is lehetnek generikusak, pl. \texttt{Box<T>}, \texttt{ApiResponse<T>}
\item \texttt{T extends ...} -- a generikus típus korlátozása egy adott "alakra"
\end{itemize}
\end{block}
\end{frame}
@@ -0,0 +1,78 @@
\section{Utility Types}
\begin{frame}[fragile]{Miért kellenek a Utility Type-ok?}
\footnotesize
A TypeScript beépít néhány \textbf{beépített generikus segédtípust} (utility type), amikkel egy meglévő típusból egy \emph{módosított változatot} tudunk létrehozni -- anélkül, hogy a mezőket kézzel újra le kellene írnunk.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
interface User {
id: number;
name: string;
email: string;
}
\end{lstlisting}
A következő diákon ebből az egy \texttt{User} interfészből építünk különböző, gyakran szükséges változatokat -- pl. "frissítéshez", "listázáshoz" vagy "létrehozáshoz".
\end{frame}
\begin{frame}[fragile]{\texttt{Partial} és \texttt{Required}}
\footnotesize
\begin{lstlisting}[language=JavaScript,style=NodeJS]
type UserUpdate = Partial<User>;
// { id?: number; name?: string; email?: string; }
type UserStrict = Required<User>;
// minden mezo kotelezo, meg ha eredetileg ? volt is
\end{lstlisting}
\begin{itemize}
\item \textbf{\texttt{Partial<T>}:} minden mezőt opcionálissá tesz -- tökéletes egy \texttt{PATCH} végpont bemenetéhez, ahol nem kötelező minden mezőt megadni
\item \textbf{\texttt{Required<T>}:} minden mezőt kötelezővé tesz -- a \texttt{Partial} ellentéte
\end{itemize}
\end{frame}
\begin{frame}[fragile]{\texttt{Pick} és \texttt{Omit}}
\footnotesize
Ez a két típus mezőket \emph{válogat ki}, vagy éppen \emph{hagy ki} egy meglévő típusból.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
type UserPreview = Pick<User, 'id' | 'name'>;
// { id: number; name: string; }
type UserWithoutEmail = Omit<User, 'email'>;
// { id: number; name: string; }
\end{lstlisting}
\begin{block}{Gyakori felhasználás}
Egy adatbázisból visszakapott \texttt{User} rekordot gyakran \emph{nem} akarunk teljes egészében visszaküldeni az API válaszban (pl. egy jelszó-hash mezőt biztosan nem) -- a \texttt{Pick}/\texttt{Omit} pontosan erre való.
\end{block}
\end{frame}
\begin{frame}[fragile]{\texttt{Record} és \texttt{Readonly}}
\footnotesize
\begin{lstlisting}[language=JavaScript,style=NodeJS]
type UsersById = Record<number, User>;
// { [id: number]: User } - kulcs-ertek par tipusa
type ImmutableUser = Readonly<User>;
// minden mezo csak olvashato (readonly)
\end{lstlisting}
\begin{itemize}
\item \textbf{\texttt{Record<K, V>}:} egy objektum típusa, ahol a kulcsok \texttt{K}, az értékek \texttt{V} típusúak -- gyakori egy azonosító szerint indexelt "gyorsítótárnál"
\item \textbf{\texttt{Readonly<T>}:} minden mezőt \texttt{readonly}-vá tesz -- hasznos, ha biztosítani akarjuk, hogy egy objektum ne módosuljon
\end{itemize}
\end{frame}
\begin{frame}{Összefoglalás -- Utility Types}
\footnotesize
\begin{block}{Utility Types}
\begin{itemize}
\item \texttt{Partial<T>} / \texttt{Required<T>} -- mezők opcionálissá / kötelezővé tétele
\item \texttt{Pick<T, K>} / \texttt{Omit<T, K>} -- mezők kiválogatása / kihagyása
\item \texttt{Record<K, V>} -- kulcs-érték típusú objektum
\item \texttt{Readonly<T>} -- minden mező írásvédetté tétele
\item Mindegyik egy meglévő típusból épít egy \emph{módosított változatot} -- kódismétlés nélkül
\end{itemize}
\end{block}
\end{frame}
@@ -0,0 +1,122 @@
\section{Enum/Osztályok}
\begin{frame}[fragile]{Enum alapok}
\footnotesize
Az \texttt{enum} (felsorolt típus) egy névvel ellátott, korlátozott értékkészletet definiál -- hasonló célt szolgál, mint a korábban látott string literal union, de saját névtérrel és futásidejű objektummal is jár.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
enum Role {
Admin, // 0
Editor, // 1
Viewer, // 2
}
enum Status {
Pending = 'pending',
Active = 'active',
Closed = 'closed',
}
const myRole: Role = Role.Admin;
\end{lstlisting}
Numerikus enumnál automatikusan 0-tól számozódnak az értékek; string enumnál mi adjuk meg a konkrét szöveges értéket -- ez utóbbi jobban olvasható naplókban, hibaüzenetekben.
\end{frame}
\begin{frame}[fragile]{Osztályok TypeScript-ben}
\footnotesize
A TypeScript osztályai a JavaScript ES6 osztályaira épülnek, kiegészítve a mezők típusozásával.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Szia, ${this.name} vagyok!`;
}
}
const p = new Person('Anna', 25);
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]{Access modifierek}
\footnotesize
Az osztály mezőihez és metódusaihoz \textbf{láthatósági szinteket} rendelhetünk -- ez segít elrejteni az implementációs részleteket, és csak a szándékolt "publikus felületet" mutatni kifelé.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
class BankAccount {
private balance: number = 0;
public readonly owner: string;
constructor(owner: string) {
this.owner = owner;
}
public deposit(amount: number): void {
this.balance += amount;
}
}
\end{lstlisting}
\begin{itemize}
\item \textbf{\texttt{public}} (alapértelmezett): bárhonnan elérhető
\item \textbf{\texttt{private}}: csak az osztályon belülről érhető el
\item \textbf{\texttt{protected}}: az osztályon és leszármazottjain belül érhető el
\end{itemize}
\end{frame}
\begin{frame}[fragile]{Interfész implementálása osztállyal}
\footnotesize
Egy osztály az \texttt{implements} kulcsszóval kötelezettséget vállal arra, hogy megfelel egy adott interfésznek -- a fordító ellenőrzi, hogy tényleg minden szükséges mezőt/metódust megvalósít-e.
\begin{lstlisting}[language=JavaScript,style=NodeJS]
interface Greetable {
greet(): string;
}
class Person implements Greetable {
constructor(private name: string) {}
greet(): string {
return `Szia, ${this.name} vagyok!`;
}
}
\end{lstlisting}
\end{frame}
\begin{frame}[fragile]{Decorators -- rövid kitekintés}
\footnotesize
A \textbf{decorator} egy speciális jelölés (\texttt{@valami}), amit osztályok, mezők vagy metódusok elé írhatunk, és ami futásidejű viselkedést csatol hozzájuk -- kicsit hasonlóan ahhoz, ahogy Express-ben a middleware-ek "csomagolják be" a route handlereket.
\begin{block}{Miért fontos ez most csak kitekintésként?}
A decorators ma még kísérleti funkció TypeScript-ben, de rendkívül elterjedt az ORM-ekben (pl. a későbbi heteken megismert TypeORM) -- ott \texttt{@Entity}, \texttt{@Column} jelölésekkel írjuk le az adatbázis-táblákat.
\end{block}
\begin{lstlisting}[language=JavaScript,style=NodeJS]
@Entity()
class User {
@Column()
name: string;
}
\end{lstlisting}
\end{frame}
\begin{frame}{Összefoglalás -- Enum és Osztályok}
\footnotesize
\begin{block}{Enum és Osztályok}
\begin{itemize}
\item \texttt{enum}: névvel ellátott, korlátozott értékkészlet (numerikus vagy string alapú)
\item Osztályok: mezők típusozása, konstruktor, metódusok -- az ES6 osztályokra épül
\item Access modifierek: \texttt{public} / \texttt{private} / \texttt{protected} -- a láthatóság szabályozása
\item \texttt{implements}: egy osztály kötelezettséget vállal egy interfész megvalósítására
\item Decorators: \texttt{@valami} jelölések, amik későbbi heteken (TypeORM) kerülnek elő
\end{itemize}
\end{block}
\end{frame}
@@ -0,0 +1,32 @@
\section{Összefoglalás}
\begin{frame}{Teljes összefoglalás}
\footnotesize
\begin{block}{Alapok}
Statikus típusrendszer JavaScript fölött; \texttt{tsc} fordít, \texttt{tsx}/\texttt{ts-node} közvetlenül futtat; \texttt{tsconfig.json} vezérli a fordítást (\texttt{strict} ajánlott).
\end{block}
\begin{block}{Típusok}
Primitívek, tömbök, tuple-ök; \texttt{any} kerülendő, \texttt{unknown} biztonságos; \texttt{interface}/\texttt{type}; union (\texttt{|}) és intersection (\texttt{\&}); generikák (\texttt{<T>}); utility types (\texttt{Partial}, \texttt{Pick}, stb.)
\end{block}
\begin{block}{Osztályok}
Access modifierek (\texttt{public}/\texttt{private}/\texttt{protected}), \texttt{implements}, \texttt{enum}, decorators (kitekintés a TypeORM felé)
\end{block}
\end{frame}
\begin{frame}{Hogyan tovább?}
\footnotesize
A legjobb módja a TypeScript elsajátításának a \emph{gyakorlás} -- az elmélet önmagában nem elég, próbáld ki a saját kódodban is.
\begin{itemize}
\item Alakítsd át egy régebbi, sima JavaScript projektedet fokozatosan TypeScript-re, fájlonként haladva
\item Kapcsold be a \texttt{strict} módot a \texttt{tsconfig.json}-ban -- kezdetben tűnhet szigorúnak, de hosszabb távon ez fizetődik ki igazán
\item Olvasd a hivatalos dokumentációt: \texttt{typescriptlang.org/docs} -- naprakész, sok interaktív példával
\item A tárgy további heteiben (Express.js + TypeScript, majd TypeORM) a itt tanultak folyamatosan elő fognak kerülni
\end{itemize}
\begin{alertblock}{Emlékeztető}
Ez az anyag kiegészítő, önálló tanulást szolgál -- nem számonkérendő, de a mélyebb megértés a féléves munkádat is megkönnyíti.
\end{alertblock}
\end{frame}