Kihagyás

TypeScript alapok

A gyakorlat anyaga

Ezen a gyakorlaton megismerkedünk a TypeScript-tel, beállítjuk a fejlesztői környezetet WebStorm-ban, és elkészítjük az első TypeScript programunkat.

A TypeScript egy JavaScript superset (az eredeti nyelven felüli bővítés), amely statikus típusrendszert ad a JavaScripthez. A TypeScript kód végül JavaScriptté fordul a legtöbb esetben (ezen a gyakorlaton nagyjából mindig), így bármely JavaScript környezetben futtatható lesz. A fő előnye, hogy a fordítási időben képes megtalálni a típushibákat, így biztonságosabb és karbantarthatóbb kódot írhatunk a segítségével.

  • A TypeScript fordítási időben ellenőrzi a típusokat.
  • Minden érvényes JavaScript kód egyben TypeScript kód is.
  • IDE-k jobb kódkiegészítést, refaktorálást képesek nyújtani.
  • Támogatja a modern ECMAScript szabványokat is (ezeket fordításkor megadott nyelvi szintre megfelelő kódra fordítja).

Környezet beállítása WebStorm-ban

  1. Nyissuk meg a WebStorm-ot.
  2. Create New Project vagy File -> New -> Project.
  3. Válasszunk egy könyvtárat a projektünknek (pl. ts-gyakorlat01).
  4. Válasszuk ki a TypeScriptet a Language választónál.

Package.json - alapok

A package.json a Node.js projektek konfigurációs fájlja, amely tartalmazza a projekt metaadatait, függőségeit és szkriptjeit. A WebStorm automatikusan létrehozza nekünk, de bármilyen mappából készíthetünk Node.js projektet az alábbi paranccsal.

1
npm init -y    # Gyors létrehozás alapértelmezett értékekkel

Az -y gyors létrehozást tesz lehetővé, nélküle lehetőségünk van varázsló-szerűen elkészíteni a fájl tartalmát. Telepítés után valami ilyesmit kapunk:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "name": "temp",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "type": "commonjs"
}

Ha létrehoztunk egy TypeScript projektet, a létrejövő package.json így néz ki:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "name": "${PROJECT_NAME}",
  "version": "1.0.0",
  "description": "",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc"
  },
  "dependencies": {},
  "devDependencies": {
    "typescript": "^5.5.3"
  },
  "private": true
}

Egy nagyjából alapvető TypeScript konfiguráció pedig valahogy így nézhet ki:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
{
  "name": "typescript-gyakorlat",
  "version": "1.0.0",
  "description": "TypeScript projekt",
  "main": "dist/index.js",
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc --watch"
  },
  "devDependencies": {
    "typescript": "^5.3.0",
    "@types/node": "^20.10.0"
  },
  "dependencies": {}
}

A scripts objektumban definiálhatunk egyedi parancsokat, amiket a projektnek tudnia kell, például elindulnia fejlesztői módban, futnia, buildelődnie.

1
2
3
4
5
6
7
{
  "scripts": {
    "build": "tsc",
    "start": "node dist/index.js",
    "dev": "tsc --watch"
  }
}

Ezen parancsok futtatása:

1
2
3
npm run build   # TypeScript fordítás
npm run dev     # Watch mode - automatikus újrafordítás
npm start       # Program futtatása (run nélkül!)

Megadhatjuk azokat a külső csomagokat is, amik szükségesek ahhoz, hogy a projektünk futhasson. Csak fejlesztés közben szükséges csomagok (TypeScript, típusdefiníciók) a devDependencies objektumba kerülnek, míg a futtatáshoz szükségesek a dependencies objektumba.

1
2
3
4
npm install -D typescript  # csak fejlesztéshez szükséges csomag 
npm install --save-dev @types/node

npm install boxen  # futtatáshoz szükséges csomag (de ezt nem használjuk az órán)

A telepített csomagok a node_modules könyvtárba kerülnek. Ezt sose töltsük fel verziókövető rendszerbe! Ehhez érdemes lehet a .gitignore fájlt létrehozni, vagy bővíteni az alábbiakkal:

1
2
node_modules/
dist/

Ha van egy projektünk, telepíthetjük az előre megadott függőségeket az npm install paranccsal.

1
npm install    # Telepíti a package.json-ban szereplő összes csomagot

TypeScript telepítése

Ha üres projektet hoztunk létre, esetleg kézzel szeretnénk egy projektet inicializálni, azt is megtehetjük. A projekt könyvtárában inicializáljuk az npm projektet, majd telepítsük a TypeScript-et. Ezt vagy a felületen tudjuk megtenni, vagy pedig a terminálban (View -> Tool Windows -> Terminal)

1
2
npm init -y
npm install --save-dev typescript
Ezekkel a parancsokkal létrehozhatunk egy megfelelő Node.js projektet, majd telepíthetjük a TypeScriptet. A --save-dev (röviden csak -D) flag azt jelenti, hogy a TypeScript csak fejlesztési függőség (nem kell production környezetben).

A typescript csomag maga a TypeScript fordító (compiler), amely a .ts fájljainkat JavaScript kóddá alakítja. Mivel a böngésző és a Node.js sem tud TypeScript kódot futtatni, csak JavaScript-et, így a TypeScript fordítás csak a fejlesztési folyamat része, a végső alkalmazásban már csak a lefordított kódot használjuk (ez JavaScript ezen az órán).

1
npm install -D @types/node

Ez a másik "alap" csomag, amit a TypeScript mellé erősen ajánlott telepíteni. Ez a csomag a Node.js típusdefinícióit tartalmazza. A Node.js JavaScript-ben íródott, és nem tartalmaz natívan TypeScript típusokat. A @types/node csomag lehetővé teszi, hogy TypeScript-ben típusbiztosan használjuk a Node.js beépített moduljait. Így segít az automatikus kódkiegészítésben is, illetve az IDE-k és TypeScript által biztosított lehetőségek maximális kiaknázását (aláhúzások, ugrás a kódhoz, stb.).

TypeScript konfiguráció létrehozása

A TypeScript fordító viselkedését a tsconfig.json fájlban állíthatjuk be. Hozzuk létre ezt a fájlt, ha nem létezne:

1
npx tsc --init

Ez létrehoz egy alapértelmezett tsconfig.json fájlt. Itt található egy példa, használhatjuk ezt is, vagy a generáltat:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

Fontosabb beállítások magyarázata:

  • target: Milyen JavaScript verzióra fordítódjon a projekt (ES2020, ES6, ES2016, stb.)
  • module: Milyen modul rendszert használjon (CommonJS a Node.js-hez)
  • outDir: A lefordított JavaScript fájlok helye
  • rootDir: A TypeScript forrásfájlok helye
  • strict: Szigorú típusellenőrzés bekapcsolása (ajánlott)
  • esModuleInterop: Jobb kompatibilitás az ES modulokkal

Projekt struktúra

Hozzuk létre a következő könyvtárszerkezetet:

1
2
3
4
5
6
7
8
typescript-gyakorlat/
├── src/
│   └── (TypeScript fájlok ide kerülnek)
├── dist/
│   └── (Lefordított JavaScript fájlok)
├── node_modules/
├── package.json
└── tsconfig.json

A src könyvtárat manuálisan kell létrehozni, ha nem létezik.

TypeScript Hello World program

Most, hogy beállítottuk a környezetet, készítsünk egy egyszerű "Hello World" programot TypeScript-ben.

Hozzunk létre egy hello.ts nevű fájlt a src könyvtárban:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
console.log("Hello, TypeScript!");

function greet(name: string): string {
    return `Hello, ${name}! Welcome to TypeScript!`;
}

const userName: string = "World";
const message: string = greet(userName);

console.log(message);

Figyeljük meg a különbségeket a JavaScript-hez képest:

  • A name paraméter után : string típusannotációt írtunk, ezzel adhatjuk meg a típust.
  • A függvény visszatérési értéke is típusozott (: string)
  • A változók deklarációjánál is megadtuk a típust

Program fordítása

A TypeScript fájlt először JavaScript-té kell fordítani a TypeScript Compiler (tsc) segítségével:

1
npx tsc

Ez a parancs: 1. Beolvassa a tsconfig.json beállításokat. 2. Megkeresi a src könyvtárban lévő .ts fájlokat. 3. Lefordítja őket JavaScript-re. 4. A .js fájlokat a dist könyvtárba helyezi.

Ha minden rendben van, megjelenik egy dist/hello.js fájl.

Program futtatása

Most már futtathatjuk a lefordított JavaScript fájlt Node.js-sel:

1
node dist/hello.js

Ha minden rendben van, a következő kimenetet kell látnod:

1
2
Hello, TypeScript!
Hello, World! Welcome to TypeScript!

A kapott JavaScript fájl, figyeljük meg a különbségeket:

1
2
3
4
5
6
7
8
"use strict";
console.log("Hello, TypeScript!");
function greet(name) {
    return `Hello, ${name}! Welcome to TypeScript!`;
}
const userName = "World";
const message = greet(userName);
console.log(message);

Futtatás egy lépésben

A ts-node egy olyan csomag, amely lehetővé teszi, hogy közvetlenül futtassunk TypeScript fájlokat anélkül, hogy előtte manuálisan lefordítanánk őket JavaScript-re. Fejlesztés közben ez sokat segíthez, mert így nem kell külön a fordítással bíbelődnünk. A ts-node a háttérben menet közben (on-the-fly) fordítja le a TypeScript kódot memóriában, és azonnal futtatja, nem hoz létre külön .js fájlokat a merevlemezen.

Telepítéshez a kapcsolódó csomagot kell feltennünk a fejlesztői függőségek közé.

1
npm install --save-dev ts-node

Ezt követően használhatjuk a ts-node, vagy npx ts-node paranccsal. Ez kifejezetten fejlesztői környezetben hasznos, production környezetben általában ezt nem használjuk, mert lassabb, mintha az előre lefordított fájljainkat futtatnánk.

Fordítás és futtatás - WebStorm

Habár a WebStormban lévő "Play" ikon sok esetben jól működik, TypeScript esetében csak nem régen került megvalósításra a funkció, és elég limitáltan működik jelenleg. Ha ezzel futtatjuk a programot, akkor például nincs típusellenőrzés, csak az IDE-ben lévő piros részekre számíthatunk. Szerencsére a ts-node használatával ez is megoldható, kényelmesen használhatjuk a "Play" ikont a futtatásra úgy, hogy azért a hibákról is típusokkal kapcsolatos kapunk visszajelzést.

Ehhez a zöld háromszög melletti három pontra kell kattintani, majd "Edit...", ahol a futási konfigurációt módosíthatjuk. A TypeScript loadert nyugodtan kikapcsolhatjuk, majd pedig a "Node parameters" alá jelezzük, hogy a ts-node segítségével futtatjuk a programot.

Node parameters: --require ts-node/register

Ezt követően okézzuk le az ablakot, és most már a "Play" ikon megfelelően fog működni, a típusossággal kapcsolatos hibákat is jelezni fogja.

WebStorm automatikus fordítás

A WebStorm képes automatikusan fordítani a TypeScript fájljainkat mentéskor:

  1. File -> Settings -> Languages & Frameworks -> TypeScript
  2. Válasszuk ki: "Recompile on changes"
  3. Apply és OK

Így nem kell minden alkalommal manuálisan futtatni az npx tsc parancsot.

Természetesen, ha az eddig is sokat használt zöld háromszög (play) ikont használjuk, ugyanez a folyamat megy végbe.

TypeScript alapszintaxis

Típusannotációk

A TypeScript alapvető célja, hogy típusokat adhassunk a kódunkhoz; a változókhoz, függvényparaméterekhez és visszatérési értékekhez:

1
2
3
4
5
6
7
8
9
let age: number = 25;
let myNname: string = "John";
let isStudent: boolean = true;

function add(a: number, b: number): number {
    return a + b;
}

console.log(add(10, 20)); // 30

Próbáljuk ki, hogy mi történik, ha nem megfelelő típust használunk. Például:

1
let age: number = "Egy szám";

Az npx tsc parancsot kiadva most az alábbi hibával találkozunk, az IDE által is pirosan aláhúzott sorra.

1
2
3
4
5
6
7
src/hello.ts:1:5 - error TS2322: Type 'string' is not assignable to type 'number'.

1 let age: number = "Egy szám";
      ~~~


Found 1 error in src/hello.ts:1

Típusinferencia

A TypeScript intelligensen ki tudja következtetni a típusokat, így nem mindig kötelező explicit megadni őket:

1
2
3
4
let inferredNumber = 42;        // TypeScript tudja, hogy number
let inferredString = "Hello";   // TypeScript tudja, hogy string

// inferredNumber = "text";     // HIBA! Nem lehet stringet adni number változónak

Ha a típus egyértelmű az értékadásból, nem muszáj azt kiírni.

typeof operátor TypeScript-ben

A JavaScript typeof operátora TypeScript-ben is működik futási időben:

1
2
3
4
5
6
7
let value: number = 100;

console.log(typeof value); // "number"

if (typeof value === "number") {
    console.log("Ez egy szám!");
}

Változók TypeScript-ben

TypeScript-ben ugyanúgy használhatjuk a let és const kulcsszavakat, mint JavaScript-ben:

1
2
3
4
5
let mutableValue: number = 10;
mutableValue = 20; // OK

const immutableValue: number = 30;
// immutableValue = 40; // HIBA! const változó értéke nem változtatható

Alapvető típusok

JavaScript-hez hasonlóan a TypeScript-ben sincs külön integer és float típus, minden szám number:

1
2
3
4
5
6
7
8
let integer: number = 42;
let float: number = 3.14;
let hex: number = 0xf00d;
let binary: number = 0b1010;
let octal: number = 0o744;

console.log(integer);  // 42
console.log(float);    // 3.14

String-eket idézőjelek között adhatunk meg. Template literal (backtick, Alt Gr + 7) is használható:

1
2
3
4
5
let singleQuote: string = 'Hello';
let doubleQuote: string = "World";
let templateLiteral: string = `Hello, ${singleQuote}!`;

console.log(templateLiteral); // "Hello, Hello!"

Logikai értékek: true vagy false

1
2
3
4
5
6
let isActive: boolean = true;
let hasError: boolean = false;

if (isActive) {
    console.log("A felhasználó aktív");
}

Tömböket kétféleképpen is elláthatunk típusokkal:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
// 1. módszer: Type[]
let numbers: number[] = [1, 2, 3, 4, 5];
let names: string[] = ["Alice", "Bob", "Charlie"];

// 2. módszer: Array<Type>
let moreNumbers: Array<number> = [10, 20, 30];
let moreNames: Array<string> = ["David", "Eve"];

numbers.push(6);        // OK
// numbers.push("7");   // HIBA! csak number lehet

console.log(numbers);   // [1, 2, 3, 4, 5, 6]

Gyakorlati példa

Lássunk egy példát, hogy miért hasznos a TypeScript. Az alábbi JavaScript kód tökéletesen lefut, holott nem kellene neki.

1
2
3
4
5
6
7
function calculateArea(width, height) {
    return width * height;
}

console.log(calculateArea(5, 10));        // 50 - OK
console.log(calculateArea("5", "10"));    // "510" - NEM OK, de nem kapunk hibát!
console.log(calculateArea(5));            // NaN - undefined * 5

Ugyanez TypeScript kód esetében már nem fordulhat elő velünk, nem kapunk ilyen jellegű észrevétlen hibákat.

1
2
3
4
5
6
7
function calculateArea(width: number, height: number): number {
    return width * height;
}

console.log(calculateArea(5, 10));        // 50 - OK
// console.log(calculateArea("5", "10")); // FORDÍTÁSI HIBA!
// console.log(calculateArea(5));         // FORDÍTÁSI HIBA! hiányzó paraméter

A TypeScript fordítási időben megtalálja ezeket a hibákat, nem kell megvárni a futási időt!

Gyakorló feladatok

  1. Írj egy pozitivSzamok nevű függvényt, amely egy számokat tartalmazó tömböt kap paraméterül. A függvény adja vissza egy új tömbben csak a pozitív számokat.
  2. Írj egy sumArray nevű függvényt, amely egy számtömböt kap és visszaadja az elemek összegét.
  3. Írj egy ervenyesEmail nevű függvényt, amely egy stringet kap paraméterül. A függvény térjen vissza true értékkel, ha a string tartalmaz '@' karaktert és '.' karaktert, különben false-szal.
  4. Írj egy szamjegyekOsszege nevű függvényt, amely egy pozitív egész számot kap paraméterül. A függvény adja vissza a szám számjegyeinek összegét.
  5. Géza mindig számolgatja, hány nap múlva lesz hétvége. Írj egy hetvegeig nevű függvényt, amely a hét egy napját kapja paraméterül (csupa kisbetűs magyar napnév, ékezetek nélkül: 'hetfo', 'kedd', 'szerda', 'csutortok', 'pentek', 'szombat', 'vasarnap').
  6. Írj egy kozosElemek nevű függvényt, amely két stringeket tartalmazó tömböt kap paraméterül. A függvény adja vissza egy tömbben azokat az elemeket, amelyek mindkét tömbben szerepelnek. Az eredmény tömb ne tartalmazzon duplikátumokat.
  7. Készítsünk egy szavazó alkalmazást! Írj egy szavazasEredmeny nevű függvényt, amely egy stringet kap paraméterül. Ez a string pontosvesszőkkel elválasztva tartalmazza a leadott szavazatokat ('igen' vagy 'nem'). A string előtt és után lehetnek whitespace karakterek, amiket el kell távolítani. A függvény térjen vissza egy objektummal, amely tartalmazza az igen és nem szavazatok számát, valamint az eredményt szövegesen:
    • Ha több az igen szavazat: 'elfogadva'
    • Ha több a nem szavazat: 'elutasitva'
    • Ha egyenlő: 'dontetlen'
    • Ha a paraméter üres string vagy nem megfelelő típusú: 'ervenytelen'

Hasznos parancsok összefoglalása

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# TypeScript telepítés
npm install --save-dev typescript

# tsconfig.json létrehozása
npx tsc --init

# TypeScript fordítás (összes fájl)
npx tsc

# Egy konkrét fájl fordítása
npx tsc src/hello.ts

# Watch mode (automatikus újrafordítás változáskor)
npx tsc --watch

# Lefordított JavaScript futtatása
node dist/hello.js

# Egy lépéses futtatás
ts-node src/hello.ts