third week
This commit is contained in:
1 parent
95e62c915f
commit
a935851e22
31 files changed
+1269
-33
No files matched your search
@@ -179,6 +179,7 @@ megtekintéséhez!},icon=Paperclip}
|
|||||||
}
|
}
|
||||||
|
|
||||||
% Kisebb betűméret a slide-okhoz
|
% Kisebb betűméret a slide-okhoz
|
||||||
|
\setbeamerfont{section in head/foot}{size=\tiny}
|
||||||
\setbeamerfont{frametitle}{size=\normalsize}
|
\setbeamerfont{frametitle}{size=\normalsize}
|
||||||
\setbeamerfont{framesubtitle}{size=\small}
|
\setbeamerfont{framesubtitle}{size=\small}
|
||||||
\setbeamerfont{block title}{size=\small}
|
\setbeamerfont{block title}{size=\small}
|
||||||
|
|||||||
@@ -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}
|
||||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
|||||||
|
\documentclass[aspectratio=169]{beamer}
|
||||||
|
\usepackage{../common/webfejl}
|
||||||
|
|
||||||
|
\title{TypeScript}
|
||||||
|
\subtitle{Kiegészítő tananyag -- a JavaScript alapoktól a haladó típusokig}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\titlepage
|
||||||
|
\logoalul
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\input{sections/00-bevezetes}
|
||||||
|
\input{sections/01-alaptipusok}
|
||||||
|
\input{sections/02-interface-type}
|
||||||
|
\input{sections/03-fuggvenyek}
|
||||||
|
\input{sections/04-union-intersection}
|
||||||
|
\input{sections/05-generics}
|
||||||
|
\input{sections/06-utility-types}
|
||||||
|
\input{sections/07-enums-classes}
|
||||||
|
\input{sections/08-osszefoglalas}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
Binary file not shown.
@@ -37,8 +37,8 @@ Az első hat hét az alapozásról szól: megismerjük a HTTP protokollt, felép
|
|||||||
\begin{block}{}
|
\begin{block}{}
|
||||||
\begin{tabular}{ll}
|
\begin{tabular}{ll}
|
||||||
\textbf{1. hét} & Alapok, Node.js, HTTPS, NPM package manager \\
|
\textbf{1. hét} & Alapok, Node.js, HTTPS, NPM package manager \\
|
||||||
\textbf{2. hét} & REST API, architekturális felépítések, CQRS \\
|
\textbf{2. hét} & Express.js, Routing, Postman, TypeScript \\
|
||||||
\textbf{3. hét} & Express.js, Routing, Postman, TypeScript \\
|
\textbf{3. hét} & REST API, architekturális felépítések, CQRS \\
|
||||||
\textbf{4. hét} & ORM, database connection, CRUD operations \\
|
\textbf{4. hét} & ORM, database connection, CRUD operations \\
|
||||||
\textbf{5. hét} & TypeORM, Aggregates, Repositories \\
|
\textbf{5. hét} & TypeORM, Aggregates, Repositories \\
|
||||||
\textbf{6. hét} & \textbf{ZH 1}: Database CRUD, ORM, CQRS + DTOs, Mappers \\
|
\textbf{6. hét} & \textbf{ZH 1}: Database CRUD, ORM, CQRS + DTOs, Mappers \\
|
||||||
|
|||||||
@@ -8,6 +8,6 @@ A mai órán megismertük a webes kommunikáció alapjait, elindítottunk egy No
|
|||||||
\item \textbf{Fejlesztői környezet telepítése} (Node.js, npm) -- ha még nincs telepítve, mindenképp pótold otthon
|
\item \textbf{Fejlesztői környezet telepítése} (Node.js, npm) -- ha még nincs telepítve, mindenképp pótold otthon
|
||||||
\item \textbf{Git és verziókezelés alapjai} -- a kódunkat innentől mindig verziókezelve fogjuk fejleszteni
|
\item \textbf{Git és verziókezelés alapjai} -- a kódunkat innentől mindig verziókezelve fogjuk fejleszteni
|
||||||
\item \textbf{Első Express.js alkalmazás elkészítése} -- a jövő héten a beépített \texttt{http} modul helyett egy sokkal kényelmesebb keretrendszert használunk
|
\item \textbf{Első Express.js alkalmazás elkészítése} -- a jövő héten a beépített \texttt{http} modul helyett egy sokkal kényelmesebb keretrendszert használunk
|
||||||
\item \textbf{REST API alapok megismerése} (2. hét) -- ráépítünk a mai HTTP-s tudásra
|
\item \textbf{REST API alapok megismerése} (3. hét) -- miután megírtuk az első route-jainkat, rátérünk a tudatos tervezésükre is
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
\end{frame}
|
\end{frame}
|
||||||
Binary file not shown.
@@ -0,0 +1,21 @@
|
|||||||
|
\documentclass[aspectratio=169]{beamer}
|
||||||
|
\usepackage{../common/webfejl}
|
||||||
|
|
||||||
|
\title{Webtechnológia és webalkalmazás-fejlesztés}
|
||||||
|
\subtitle{2. hét -- Express.js, Routing, Postman, TypeScript}
|
||||||
|
|
||||||
|
\begin{document}
|
||||||
|
|
||||||
|
\begin{frame}
|
||||||
|
\titlepage
|
||||||
|
\logoalul
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\input{sections/00-visszatekintes}
|
||||||
|
\input{sections/01-expressjs}
|
||||||
|
\input{sections/02-routing}
|
||||||
|
\input{sections/03-postman}
|
||||||
|
\input{sections/04-typescript}
|
||||||
|
\input{sections/05-kovetkezo-lepesek}
|
||||||
|
|
||||||
|
\end{document}
|
||||||
@@ -2,30 +2,31 @@
|
|||||||
|
|
||||||
\begin{frame}{Miről volt szó múlt héten?}
|
\begin{frame}{Miről volt szó múlt héten?}
|
||||||
\footnotesize
|
\footnotesize
|
||||||
Az első héten megismertük a webes kommunikáció alapköveit: a HTTP protokollt, a Node.js futtatókörnyezetet és az npm csomagkezelőt. Ezek nélkül nem tudnánk elindulni -- de önmagukban még nem elegendők egy jól használható backend rendszer megépítéséhez.
|
Az első héten megismertük a webes kommunikáció alapjait: a HTTP protokollt, a Node.js futtatókörnyezetet, és elindítottunk egy egyszerű szervert a beépített \texttt{http} modullal. Emellett megtanultuk, hogyan kezeljük npm-mel a projekt függőségeit.
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{HTTP/HTTPS:} Request-Response modell, állapotmentes protokoll, metódusok (GET, POST, PUT, PATCH, DELETE), státuszkódok
|
\item \textbf{HTTP:} Request-Response modell, metódusok (GET, POST, PUT, PATCH, DELETE), státuszkódok
|
||||||
\item \textbf{Node.js:} szerveroldali JavaScript futtatókörnyezet, beépített \texttt{http} modul egy egyszerű szerver elindítására
|
\item \textbf{Node.js:} \texttt{http.createServer} -- egy függvény, ami minden kérést kiszolgál
|
||||||
\item \textbf{npm:} csomagkezelés, \texttt{package.json}, függőségek
|
\item \textbf{npm:} \texttt{package.json}, csomagok telepítése, \texttt{npm scripts}
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
\begin{block}{A hiányzó darab}
|
\begin{block}{A hiányzó darab}
|
||||||
Múlt héten a \texttt{http.createServer}-rel csak egyetlen, mindent kiszolgáló függvényt írtunk. Éles alkalmazásoknál ez gyorsan kezelhetetlenné válik -- szükségünk van egy \emph{tervezési logikára}, ami megmondja, hogyan szervezzük az útvonalainkat, adatainkat és a kódunk rétegeit.
|
A nyers \texttt{http} modullal minden egyes útvonalat, minden HTTP metódust és a kérés törzsének feldolgozását is kézzel, \texttt{if-else} ágakkal kellene megírnunk. Ez pár végpontnál még kezelhető, de gyorsan átláthatatlanná válik.
|
||||||
\end{block}
|
\end{block}
|
||||||
\end{frame}
|
\end{frame}
|
||||||
|
|
||||||
\begin{frame}{Mi a célja a mai órának?}
|
\begin{frame}{Mi a célja a mai órának?}
|
||||||
\footnotesize
|
\footnotesize
|
||||||
A mai anyag három, egymásra épülő témát dolgoz fel: hogyan tervezzünk jól használható API-kat, hogyan szervezzük a backend kódunkat rétegekbe, és hogyan válasszuk szét az írási és olvasási műveleteket, ha a rendszerünk ezt megkívánja.
|
A mai órán egy valódi keretrendszert vezetünk be, amivel a múlt heti kézi munkát nagyrészt kiváltjuk, majd megtanuljuk, hogyan teszteljük és hogyan típusozzuk a kódunkat.
|
||||||
|
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{REST API alapok} -- hogyan tervezzünk következetes, könnyen használható API-t
|
\item \textbf{Express.js} -- a legelterjedtebb Node.js webes keretrendszer, ami elveszi a routing és a kérésfeldolgozás nagy részének terhét
|
||||||
\item \textbf{Architekturális felépítések} -- hogyan szervezzük a backend kódunkat rétegekbe, hogy karbantartható maradjon
|
\item \textbf{Routing} -- hogyan szervezzük az útvonalainkat, hogyan olvassuk ki a kérés adatait
|
||||||
\item \textbf{CQRS} -- mikor éri meg szétválasztani az írási és olvasási műveleteket
|
\item \textbf{Postman} -- hogyan teszteljük a saját API-nkat anélkül, hogy frontendet kellene hozzá írnunk
|
||||||
|
\item \textbf{TypeScript} -- hogyan adjunk típusbiztonságot a JavaScript kódunkhoz
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
\begin{alertblock}{Fontos}
|
\begin{alertblock}{Fontos}
|
||||||
A mai témák még mindig \emph{tervezési} kérdések -- konkrét kódot Express.js-ben csak a jövő héttől írunk. Most a "miért így?" kérdésre keressük a választ, hogy a következő heti kód már értelmes döntéseken alapuljon.
|
Ma még mindig egyszerű, tárolás nélküli (memóriában tartott) példákkal dolgozunk -- az adatbázis-kapcsolat csak a 4. héten kerül képbe. Most a keretrendszerre és az eszközökre fókuszálunk.
|
||||||
\end{alertblock}
|
\end{alertblock}
|
||||||
\end{frame}
|
\end{frame}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
\section{Express.js}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Miért nem elég a nyers \texttt{http} modul?}
|
||||||
|
\footnotesize
|
||||||
|
Emlékezzünk vissza az 1. héten írt szerverre: egyetlen callback-ben kellett eldöntenünk, milyen útvonalra és milyen metódussal érkezett a kérés.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
const server = http.createServer((req, res) => {
|
||||||
|
if (req.url === '/users' && req.method === 'GET') {
|
||||||
|
// ...
|
||||||
|
} else if (req.url === '/users' && req.method === 'POST') {
|
||||||
|
// ...
|
||||||
|
} else if (req.url.startsWith('/users/') && req.method === 'GET') {
|
||||||
|
// egy adott felhasznalo -- id kezzel kivagva az URL-bol
|
||||||
|
} // es igy tovabb...
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\begin{alertblock}{A probléma}
|
||||||
|
Tíz-húsz végpontnál ez a lánc kezelhetetlenné válik: nincs beépített segítség a paraméterek kiolvasásához, a JSON törzs feldolgozásához, vagy az útvonalak csoportosításához.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Mi az Express.js?}
|
||||||
|
\footnotesize
|
||||||
|
Az \textbf{Express.js} a legelterjedtebb Node.js webes keretrendszer (web framework): a nyers \texttt{http} modulra épül, és eltünteti a kézi \texttt{if-else} láncokat egy sokkal kényelmesebb, deklaratív routing API-val.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Egyszerű, minimalista -- nem kényszerít ránk egy előírt projektszerkezetet
|
||||||
|
\item Beépített segítség: útvonal-paraméterek, query stringek, JSON válaszok
|
||||||
|
\item Hatalmas ökoszisztéma: middleware-ek szinte minden problémára (autentikáció, naplózás, CORS, stb.)
|
||||||
|
\item Ez lesz a tárgy hátralévő részének elsődleges keretrendszere
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{block}{Fontos}
|
||||||
|
Az Express \emph{nem} helyettesíti a Node.js-t -- ráépül. Amit az 1. héten a \texttt{http} modulról tanultunk (request/response ciklus), az Express alatt is pontosan úgy működik.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Az első Express alkalmazás}
|
||||||
|
\footnotesize
|
||||||
|
Néhány sorból már működő szerverünk van -- hasonlítsd össze az előző dia \texttt{if-else} láncával!
|
||||||
|
|
||||||
|
\begin{block}{Telepítés}
|
||||||
|
\begin{verbatim}
|
||||||
|
npm install express
|
||||||
|
\end{verbatim}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
const express = require('express');
|
||||||
|
const app = express();
|
||||||
|
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.send('Hello, Express!');
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(3000, () => {
|
||||||
|
console.log('Szerver fut: http://localhost:3000');
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{A middleware fogalma}
|
||||||
|
\footnotesize
|
||||||
|
Az Express legfontosabb koncepciója a \textbf{middleware}: egy függvény, ami a kérés és a válasz között fut le, és vagy továbbengedi a folyamatot (\texttt{next()}), vagy lezárja azt egy válasszal.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
console.log(`${req.method} ${req.url}`);
|
||||||
|
next(); // tovabb a kovetkezo middleware-hez/route-hoz
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(express.json()); // JSON body-t olvaso middleware
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item A kérések egy "csövön" (pipeline) haladnak végig, middleware-eken keresztül
|
||||||
|
\item \texttt{express.json()} nélkül a \texttt{req.body} üres lenne POST/PUT kéréseknél
|
||||||
|
\item Naplózás, autentikáció, hibakezelés -- mind middleware-ként valósul meg
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Összefoglalás -- Express.js}
|
||||||
|
\footnotesize
|
||||||
|
\begin{block}{Express.js alapok}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Node.js-re épülő, minimalista webes keretrendszer
|
||||||
|
\item Kiváltja a kézi \texttt{if-else} útvonal-elágazásokat
|
||||||
|
\item \texttt{app.listen()} -- szerver indítása, ugyanaz mint a nyers \texttt{http}-nél
|
||||||
|
\item Middleware: \texttt{app.use()} -- a kérés-válasz "csövének" építőeleme
|
||||||
|
\item \texttt{express.json()} -- beépített middleware a JSON törzs feldolgozásához
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
\section{Routing}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{HTTP metódusok Express-ben}
|
||||||
|
\footnotesize
|
||||||
|
Minden HTTP metódushoz külön Express függvény tartozik, ami pontosan a metódus nevét viseli -- ez sokkal olvashatóbb, mint egy \texttt{if (req.method === ...)} lánc.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
app.get('/users', (req, res) => { /* lista */ });
|
||||||
|
app.post('/users', (req, res) => { /* letrehozas */ });
|
||||||
|
app.get('/users/:id', (req, res) => { /* egy elem */ });
|
||||||
|
app.put('/users/:id', (req, res) => { /* teljes frissites */ });
|
||||||
|
app.patch('/users/:id', (req, res) => { /* reszleges frissites */ });
|
||||||
|
app.delete('/users/:id', (req, res) => { /* torles */ });
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\begin{block}{Emlékeztető}
|
||||||
|
Ez pontosan a 2. héten (a jövő héten, amikor visszatérünk rá) tárgyalt CRUD $\to$ HTTP metódus táblázatot követi -- nem véletlen az egyezés.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Route paraméterek és query stringek}
|
||||||
|
\footnotesize
|
||||||
|
Az útvonalban lévő \texttt{:id}-hez hasonló szegmenseket \textbf{route paramétereknek} hívjuk, a kérdőjel utáni \texttt{?key=value} részt pedig \textbf{query stringnek}. Express mindkettőt készen, feldolgozva adja át.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
// GET /users/123?includeOrders=true
|
||||||
|
app.get('/users/:id', (req, res) => {
|
||||||
|
const userId = req.params.id; // "123"
|
||||||
|
const includeOrders = req.query.includeOrders; // "true"
|
||||||
|
res.json({ id: userId, includeOrders });
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{\texttt{req.params}:} az útvonal része, egy konkrét erőforrás azonosítására (\texttt{/users/:id})
|
||||||
|
\item \textbf{\texttt{req.query}:} opcionális finomítás (lapozás, szűrés, rendezés)
|
||||||
|
\end{itemize}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{A kérés törzse: \texttt{req.body}}
|
||||||
|
\footnotesize
|
||||||
|
\texttt{POST}, \texttt{PUT} és \texttt{PATCH} kéréseknél az adat jellemzően a kérés törzsében (body) érkezik JSON formátumban -- ehhez szükséges az előző órán megismert \texttt{express.json()} middleware.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
app.use(express.json());
|
||||||
|
|
||||||
|
app.post('/users', (req, res) => {
|
||||||
|
const { name, email } = req.body;
|
||||||
|
const newUser = { id: Date.now(), name, email };
|
||||||
|
users.push(newUser);
|
||||||
|
res.status(201).json(newUser);
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\begin{block}{Emlékeztető az 1. hétről}
|
||||||
|
A \texttt{201 Created} a helyes státuszkód sikeres \texttt{POST} esetén -- ezt már az 1. héten megismertük.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Útvonalak szervezése: \texttt{express.Router()}}
|
||||||
|
\footnotesize
|
||||||
|
Ahogy nő az alkalmazás, egyetlen fájlban tartani az összes route-ot áttekinthetetlenné válik. Az \texttt{express.Router()} lehetővé teszi, hogy erőforrásonként külön fájlba szervezzük az útvonalakat.
|
||||||
|
|
||||||
|
\begin{columns}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{routes/users.js}
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
const router = express.Router();
|
||||||
|
|
||||||
|
router.get('/', listUsers);
|
||||||
|
router.post('/', createUser);
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\begin{column}{0.48\textwidth}
|
||||||
|
\begin{block}{app.js}
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
const usersRouter =
|
||||||
|
require('./routes/users');
|
||||||
|
|
||||||
|
app.use(
|
||||||
|
'/users',
|
||||||
|
usersRouter
|
||||||
|
);
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{block}
|
||||||
|
\end{column}
|
||||||
|
\end{columns}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Hibakezelés alapjai}
|
||||||
|
\footnotesize
|
||||||
|
Express két speciális esetet kezel middleware-ekkel: amikor egy útvonal nem létezik, és amikor egy route handler hibát dob.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
// 404 - ha semelyik route nem illeszkedett
|
||||||
|
app.use((req, res) => {
|
||||||
|
res.status(404).json({ error: 'Nem talalhato' });
|
||||||
|
});
|
||||||
|
|
||||||
|
// hibakezelo middleware -- negy parameter jelzi Expressnek!
|
||||||
|
app.use((err, req, res, next) => {
|
||||||
|
console.error(err);
|
||||||
|
res.status(500).json({ error: 'Szerver hiba' });
|
||||||
|
});
|
||||||
|
\end{lstlisting}
|
||||||
|
|
||||||
|
\begin{block}{Fontos}
|
||||||
|
A hibakezelő middleware-t \emph{a legvégén} kell regisztrálni, az összes route után -- Express a négy paraméter alapján ismeri fel.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Összefoglalás -- Routing}
|
||||||
|
\footnotesize
|
||||||
|
\begin{block}{Routing Express-ben}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \texttt{app.get/post/put/patch/delete} -- metódusonként külön, olvasható route definíció
|
||||||
|
\item \texttt{req.params} -- útvonal paraméterek, \texttt{req.query} -- query string, \texttt{req.body} -- kérés törzse
|
||||||
|
\item \texttt{express.Router()} -- útvonalak szervezése külön fájlokba, erőforrásonként
|
||||||
|
\item 404 és hibakezelő middleware a route-ok végén
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
\section{Postman}
|
||||||
|
|
||||||
|
\begin{frame}{Mi a Postman, és miért használjuk?}
|
||||||
|
\footnotesize
|
||||||
|
Eddig minden végpontunkat vagy böngészőből (csak GET-hez alkalmas), vagy egyáltalán nem próbáltuk ki. A \textbf{Postman} egy grafikus alkalmazás, amivel bármilyen HTTP kérést összeállíthatunk és elküldhetünk -- anélkül, hogy előbb frontendet kellene írnunk hozzá.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item Bármilyen metódus (GET, POST, PUT, PATCH, DELETE) kipróbálható
|
||||||
|
\item Egyedi fejlécek (headers) és JSON törzs (body) is összeállítható
|
||||||
|
\item A választ (státuszkód, headerek, body) azonnal, formázva látjuk
|
||||||
|
\item Elmenthető, újra futtatható, csapattagokkal megosztható kéréssorozatok
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{block}{Miért nem elég a böngésző?}
|
||||||
|
A böngésző címsorába írt URL mindig \texttt{GET} kérést küld -- \texttt{POST}, \texttt{PUT}, \texttt{DELETE} kéréshez, vagy egyedi fejlécekhez (pl. \texttt{Authorization}) külön eszköz kell.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Egy kérés összeállítása}
|
||||||
|
\footnotesize
|
||||||
|
Egy Postman-kérés pontosan azokból az elemekből áll, amiket az 1. héten a HTTP kérés felépítéseként már megismertünk: metódus, URL, fejlécek, törzs.
|
||||||
|
|
||||||
|
\begin{block}{Amit beállítunk}
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Metódus:} pl. \texttt{POST}
|
||||||
|
\item \textbf{URL:} \texttt{http://localhost:3000/users}
|
||||||
|
\item \textbf{Headers:} \texttt{Content-Type: application/json}
|
||||||
|
\item \textbf{Body (raw, JSON):}
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{verbatim}
|
||||||
|
{
|
||||||
|
"name": "Kis Anna",
|
||||||
|
"email": "anna@example.com"
|
||||||
|
}
|
||||||
|
\end{verbatim}
|
||||||
|
|
||||||
|
Küldés után azonnal látjuk a választ: \texttt{201 Created} és a visszakapott, azonosítóval kiegészített felhasználó.
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Collections és Environment változók}
|
||||||
|
\footnotesize
|
||||||
|
Ahogy egyre több végpontot tesztelünk, hasznos ezeket rendszerezni, és elkerülni, hogy mindig kézzel írjuk be az alap URL-t.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Collection:} kérések egy mappába rendezett gyűjteménye -- pl. minden "Users" végpont egy collection-ben
|
||||||
|
\item \textbf{Environment:} névhez kötött változók (pl. \texttt{baseUrl}), amikkel a kéréseinkben \texttt{\{\{baseUrl\}\}/users} írható a teljes URL helyett
|
||||||
|
\item Így egyetlen változó átírásával válthatunk fejlesztői és éles környezet között
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{block}{Gyakorlati tipp}
|
||||||
|
Fejlesztés közben minden újonnan megírt végpontot azonnal próbálj ki Postmanben -- ez sokkal gyorsabb visszajelzést ad, mint egy teljes frontend felület megvárása.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Összefoglalás -- Postman}
|
||||||
|
\footnotesize
|
||||||
|
\begin{block}{Postman}
|
||||||
|
\begin{itemize}
|
||||||
|
\item Grafikus eszköz tetszőleges HTTP kérés összeállítására és elküldésére
|
||||||
|
\item Kiválthatja a böngészőt olyan esetekben, amikor nem elég a sima GET
|
||||||
|
\item Kérés = metódus + URL + headers + body -- ugyanaz, mint amit HTTP-ből már tudunk
|
||||||
|
\item Collections: kérések rendszerezése; Environments: újrafelhasználható változók
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
\section{Következő lépések}
|
|
||||||
|
|
||||||
\begin{frame}{Következő lépések}
|
|
||||||
\footnotesize
|
|
||||||
Ma megismertük, hogyan tervezzünk jól használható REST API-t, hogyan szervezzük a backend kódunkat rétegekbe, és mikor érdemes szétválasztani az írási és olvasási műveleteket. A jövő héten ezekre a tervezési elvekre már konkrét kódot építünk.
|
|
||||||
|
|
||||||
\begin{enumerate}
|
|
||||||
\item \textbf{Express.js megismerése} -- a jövő heti keretrendszer, amivel az API/Controller réteget ténylegesen megírjuk
|
|
||||||
\item \textbf{Routing gyakorlása} -- a mai órán tervezett erőforrás-orientált URL-eket Express.js route handlerekként fogjuk implementálni
|
|
||||||
\item \textbf{Postman telepítése} -- API-k manuális teszteléséhez, hogy a saját végpontjainkat is ki tudjuk próbálni
|
|
||||||
\item \textbf{TypeScript alapok} -- a jövő héttől fokozatosan áttérünk típusos JavaScriptre
|
|
||||||
\end{enumerate}
|
|
||||||
|
|
||||||
\begin{alertblock}{Javaslat}
|
|
||||||
Nézd át még egyszer a CRUD $\to$ HTTP metódus táblázatot -- a jövő héten ezt fogjuk közvetlenül route definíciókra fordítani.
|
|
||||||
\end{alertblock}
|
|
||||||
\end{frame}
|
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
\section{TypeScript}
|
||||||
|
|
||||||
|
\begin{frame}{Miért TypeScript?}
|
||||||
|
\footnotesize
|
||||||
|
A JavaScript \textbf{gyengén típusos} nyelv: egy változó bármikor bármilyen típusú értéket felvehet, és a hibák jó része csak futás közben derül ki -- gyakran éppen egy éles felhasználónál. A \textbf{TypeScript} a JavaScript egy kiterjesztése, ami \emph{statikus típusokat} ad hozzá.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item A típushibák nagy része már fordításkor (kódírás közben) kiderül, nem futás közben
|
||||||
|
\item Az IDE sokkal pontosabb kódkiegészítést és hibajelzést tud adni
|
||||||
|
\item Nagyobb csapatban, nagyobb kódbázisban különösen megtérül
|
||||||
|
\item A TypeScript kód JavaScript-té fordul le -- a böngésző/Node.js sosem futtat TS-t közvetlenül
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{block}{Fontos}
|
||||||
|
A TypeScript \emph{opcionális} réteg a JavaScript fölött -- minden érvényes JavaScript kód (majdnem) érvényes TypeScript is, fokozatosan vezethető be egy meglévő projektbe.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Telepítés és futtatás}
|
||||||
|
\footnotesize
|
||||||
|
A TypeScript fájlok (\texttt{.ts} kiterjesztéssel) nem futtathatók közvetlenül Node.js-szel -- előbb JavaScript-té kell őket fordítani, vagy egy erre képes futtatót kell használnunk.
|
||||||
|
|
||||||
|
\begin{block}{Fordítás \texttt{tsc}-vel}
|
||||||
|
\begin{verbatim}
|
||||||
|
npm install -D typescript
|
||||||
|
npx tsc --init # letrehozza a tsconfig.json-t
|
||||||
|
npx tsc # lefordit minden .ts fajlt .js-re
|
||||||
|
\end{verbatim}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{block}{Fejlesztés közben: \texttt{tsx}}
|
||||||
|
\begin{verbatim}
|
||||||
|
npx tsx index.ts
|
||||||
|
\end{verbatim}
|
||||||
|
A \texttt{tsx} fordítás nélkül, közvetlenül futtatja a TypeScript fájlt -- fejlesztés közben ez a kényelmesebb.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}[fragile]{Alaptípusok és egy egyszerű interfész}
|
||||||
|
\footnotesize
|
||||||
|
A típusokat a változó/paraméter neve után, kettősponttal adjuk meg. Több mező közös leírásához pedig egy \texttt{interface}-t érdemes definiálni.
|
||||||
|
|
||||||
|
\begin{lstlisting}[language=JavaScript,style=NodeJS]
|
||||||
|
let name: string = 'Anna';
|
||||||
|
let age: number = 25;
|
||||||
|
let isActive: boolean = true;
|
||||||
|
let tags: string[] = ['admin', 'user'];
|
||||||
|
|
||||||
|
interface User {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function greet(user: User): string {
|
||||||
|
return `Hello, ${user.name}!`;
|
||||||
|
}
|
||||||
|
\end{lstlisting}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Összefoglalás -- TypeScript}
|
||||||
|
\footnotesize
|
||||||
|
\begin{block}{TypeScript alapok}
|
||||||
|
\begin{itemize}
|
||||||
|
\item A JavaScript statikus típusokkal kiegészített változata -- fordításkor szűri a típushibákat
|
||||||
|
\item \texttt{tsc} -- fordítás JavaScript-té; \texttt{tsx}/\texttt{ts-node} -- közvetlen futtatás fejlesztéskor
|
||||||
|
\item Alaptípusok: \texttt{string}, \texttt{number}, \texttt{boolean}, tömbök
|
||||||
|
\item \texttt{interface} -- több mezőből álló adatszerkezet típusának leírása
|
||||||
|
\end{itemize}
|
||||||
|
\end{block}
|
||||||
|
|
||||||
|
\begin{alertblock}{Több is van!}
|
||||||
|
Ez csak az alapok -- a generikus típusokról, a \texttt{union}/\texttt{intersection} típusokról és más haladó fogalmakról egy \emph{külön, önállóan is végigkövethető} kiegészítő anyagban olvashatsz, ha mélyebben el szeretnél merülni.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
\section{Következő lépések}
|
||||||
|
|
||||||
|
\begin{frame}{Következő lépések}
|
||||||
|
\footnotesize
|
||||||
|
Ma megírtuk az első valódi Express.js alkalmazásunkat, megtanultuk, hogyan teszteljük Postmannel, és belekóstoltunk a TypeScriptbe. Eddig azonban inkább \emph{gyakorlati megérzés} alapján dolgoztunk -- a jövő héten ezt tudatos tervezéssé alakítjuk.
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \textbf{REST API tervezési elvek} -- visszatekintünk a ma írt útvonalainkra: jól neveztük-e el őket?
|
||||||
|
\item \textbf{Architekturális felépítések} -- hogyan válasszuk szét a route handlert (amit ma írtunk) az üzleti logikától
|
||||||
|
\item \textbf{CQRS} -- mikor éri meg külön kezelni az írási és olvasási műveleteket
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\begin{alertblock}{Javaslat}
|
||||||
|
Nézd át még egyszer a ma írt route-jaidat -- a jövő héten pontosan ezekre a konkrét példákra fogunk tervezési elveket alkalmazni.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
Binary file not shown.
@@ -2,7 +2,7 @@
|
|||||||
\usepackage{../common/webfejl}
|
\usepackage{../common/webfejl}
|
||||||
|
|
||||||
\title{Webtechnológia és webalkalmazás-fejlesztés}
|
\title{Webtechnológia és webalkalmazás-fejlesztés}
|
||||||
\subtitle{2. hét -- REST API, Architekturális felépítések, CQRS}
|
\subtitle{3. hét -- REST API, Architekturális felépítések, CQRS}
|
||||||
|
|
||||||
\begin{document}
|
\begin{document}
|
||||||
|
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
\section{Visszatekintés}
|
||||||
|
|
||||||
|
\begin{frame}{Miről volt szó múlt héten?}
|
||||||
|
\footnotesize
|
||||||
|
Az első héten megismertük a webes kommunikáció alapköveit (HTTP, Node.js, npm), a másodikon pedig ezekre építve megírtuk az első valódi backend kódunkat: Express.js-szel útvonalakat (route-okat) hoztunk létre, Postmannel kipróbáltuk őket, és belekóstoltunk a TypeScriptbe is.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{Express.js:} \texttt{app.get/post/put/delete} route-ok, middleware-ek, \texttt{express.Router()}
|
||||||
|
\item \textbf{Routing:} \texttt{req.params}, \texttt{req.query}, \texttt{req.body} kezelése, JSON válaszok
|
||||||
|
\item \textbf{Postman:} kérések összeállítása és tesztelése kód írása nélkül
|
||||||
|
\item \textbf{TypeScript:} alaptípusok, egy egyszerű interfész, fordítás \texttt{tsc}-vel
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{block}{A hiányzó darab}
|
||||||
|
Múlt héten \emph{gyakorlati megérzés} alapján neveztük el az útvonalainkat és szerveztük a kódunkat. Ez működött, de nem feltétlenül a legjobb, legkövetkezetesebb módon -- ma megnézzük, milyen \emph{tervezési elvek} állnak a jó API-k és a jó backend-architektúrák mögött.
|
||||||
|
\end{block}
|
||||||
|
\end{frame}
|
||||||
|
|
||||||
|
\begin{frame}{Mi a célja a mai órának?}
|
||||||
|
\footnotesize
|
||||||
|
A mai anyag három, egymásra épülő témát dolgoz fel: hogyan tervezzünk jól használható API-kat, hogyan szervezzük a backend kódunkat rétegekbe, és hogyan válasszuk szét az írási és olvasási műveleteket, ha a rendszerünk ezt megkívánja.
|
||||||
|
|
||||||
|
\begin{itemize}
|
||||||
|
\item \textbf{REST API alapok} -- hogyan tervezzünk következetes, könnyen használható API-t
|
||||||
|
\item \textbf{Architekturális felépítések} -- hogyan szervezzük a backend kódunkat rétegekbe, hogy karbantartható maradjon
|
||||||
|
\item \textbf{CQRS} -- mikor éri meg szétválasztani az írási és olvasási műveleteket
|
||||||
|
\end{itemize}
|
||||||
|
|
||||||
|
\begin{alertblock}{Fontos}
|
||||||
|
A múlt héten megírt Express.js route-jaink jó kiindulópontok, de a mai órán \emph{visszamenőleg is} átgondoljuk őket: melyik URL-t neveznénk most másképp, hol hiányzik egy réteg, hol keverednek a felelősségek. A "miért így?" kérdésre keressük a választ, hogy a jövőben már tudatosan jól tervezett kódot írjunk.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
File renamed without changes.
+2
-2
@@ -18,7 +18,7 @@ A rétegzett felépítésnél a kódot vízszintes rétegekbe szervezzük, ahol
|
|||||||
|
|
||||||
\begin{block}{}
|
\begin{block}{}
|
||||||
\begin{itemize}
|
\begin{itemize}
|
||||||
\item \textbf{API / Controller:} fogadja a HTTP kéréseket, validálja a bemenetet, meghívja az Application réteget, visszaadja a választ -- ezt Express.js-ben írjuk meg a jövő héten
|
\item \textbf{API / Controller:} fogadja a HTTP kéréseket, validálja a bemenetet, meghívja az Application réteget, visszaadja a választ -- ezt Express.js-ben már megírtuk a múlt héten
|
||||||
\item \textbf{Application:} az egyes use case-eket (parancsokat/lekérdezéseket) orchesztrálja -- ide kerülnek majd a CQRS command/query handlerek
|
\item \textbf{Application:} az egyes use case-eket (parancsokat/lekérdezéseket) orchesztrálja -- ide kerülnek majd a CQRS command/query handlerek
|
||||||
\item \textbf{Domain:} a tiszta üzleti logika, entitások, aggregates -- semmilyen technikai függősége nincs, nem "tudja", hogy Express-ben vagy adatbázissal fut
|
\item \textbf{Domain:} a tiszta üzleti logika, entitások, aggregates -- semmilyen technikai függősége nincs, nem "tudja", hogy Express-ben vagy adatbázissal fut
|
||||||
\end{itemize}
|
\end{itemize}
|
||||||
@@ -52,7 +52,7 @@ Az MVC (Model-View-Controller) a rétegzett architektúra egyik legismertebb kon
|
|||||||
\end{itemize}
|
\end{itemize}
|
||||||
|
|
||||||
\begin{block}{Backend kontextusban}
|
\begin{block}{Backend kontextusban}
|
||||||
Amikor a jövő héten Express.js-ben "route handler"-t írunk, az gyakorlatilag az API/Controller réteg szerepét tölti be: fogadja a HTTP kérést, és delegálja a munkát az Application rétegnek.
|
Amikor a múlt héten Express.js-ben "route handler"-t írtunk, az gyakorlatilag az API/Controller réteg szerepét töltötte be: fogadja a HTTP kérést, és delegálja a munkát az Application rétegnek.
|
||||||
\end{block}
|
\end{block}
|
||||||
\end{frame}
|
\end{frame}
|
||||||
|
|
||||||
File renamed without changes.
@@ -0,0 +1,16 @@
|
|||||||
|
\section{Következő lépések}
|
||||||
|
|
||||||
|
\begin{frame}{Következő lépések}
|
||||||
|
\footnotesize
|
||||||
|
Ma megismertük, hogyan tervezzünk jól használható REST API-t, hogyan szervezzük a backend kódunkat rétegekbe, és mikor érdemes szétválasztani az írási és olvasási műveleteket. Eddig azonban minden adatunk csak a memóriában élt -- a jövő héten ezen változtatunk.
|
||||||
|
|
||||||
|
\begin{enumerate}
|
||||||
|
\item \textbf{ORM megismerése} -- egy eszköz, ami a Domain rétegünk objektumait az adatbázis tábláira képezi le, SQL írása nélkül
|
||||||
|
\item \textbf{Adatbázis-kapcsolat kiépítése} -- az Infrastructure réteg végre ténylegesen egy adatbázishoz kapcsolódik
|
||||||
|
\item \textbf{CRUD műveletek} -- a ma megtervezett \texttt{POST}/\texttt{GET}/\texttt{PUT}/\texttt{DELETE} végpontok mögé valódi, perzisztens adatkezelést építünk
|
||||||
|
\end{enumerate}
|
||||||
|
|
||||||
|
\begin{alertblock}{Javaslat}
|
||||||
|
Gondold át még egyszer az öt rétegű architektúrát -- a jövő héten pontosan az Infrastructure réteget töltjük meg valódi tartalommal.
|
||||||
|
\end{alertblock}
|
||||||
|
\end{frame}
|
||||||
Reference in new issue
Block a user