Kurs JavaScript i TypeScript · Moduł 6: Podstawy TypeScript
Kompilator TypeScript (tsc)
W tej lekcji13
Wprowadzenie: Laboratorium InGen
Witaj w laboratorium genetycznym InGen! Podobnie jak naukowcy parku przekształcają starożytne sekwencje DNA w żywe dinozaury, kompilator TypeScript przekształca kod TypeScript w standardowy JavaScript, który może być uruchomiony w przeglądarce lub środowisku Node.js.
Dr. Wu, główny genetyk parku, często powtarza: "Dajemy życie kodom sprzed milionów lat". Podobnie kompilator TypeScript pozwala nam pisać kod w nowoczesnym języku, który jest następnie przekształcany w bardziej "pierwotną" formę JavaScript, zrozumiałą dla wszystkich środowisk.
Czym jest kompilator TypeScript?
Kompilator TypeScript (zwykle nazywany tsc - TypeScript Compiler) to narzędzie, które analizuje twój kod TypeScript, sprawdza typy, a następnie generuje odpowiadający mu kod JavaScript. Jest to mostem między światem statycznie typowanego TypeScript a dynamicznie typowanym JavaScriptem.
W terminologii Parku Jurajskiego, możemy powiedzieć, że:
- Kod TypeScript to jak wyizolowane sekwencje DNA dinozaura
- Proces kompilacji to jak procedura klonowania
- Wygenerowany kod JavaScript to żywy dinozaur, gotowy do wypuszczenia do parku
Podstawy używania kompilatora tsc
Instalacja kompilatora
Zanim zaczniemy eksperymenty genetyczne, potrzebujemy odpowiedniego sprzętu:
1# Instalacja globalna (daje dostęp do komendy tsc w całym systemie)
2npm install -g typescript
3
4# Instalacja lokalna (tylko w obrębie projektu)
5npm install --save-dev typescriptKompilacja pojedynczego pliku
Kompilowanie pojedynczego pliku TypeScript to jak tworzenie pojedynczego dinozaura w laboratorium:
1# Składnia: tsc [nazwa-pliku].ts
2tsc security-system.tsPo wykonaniu tej komendy, zostanie utworzony plik JavaScript o tej samej nazwie: security-system.js.
Kompilacja całego projektu
W prawdziwym Parku Jurajskim nie tworzymy pojedynczych dinozaurów - budujemy cały ekosystem. Podobnie, w większych projektach kompilujemy wiele plików na raz:
1# Kompilacja całego projektu zgodnie z konfiguracją tsconfig.json
2tsc
3
4# Kompilacja z obserwowaniem zmian (tryb watch) - kompilator będzie śledził zmiany i rekompilował kod automatycznie
5tsc --watchProces kompilacji
Proces kompilacji TypeScript przebiega w kilku krokach, podobnie jak proces tworzenia dinozaura w laboratorium:
- Skanowanie - kompilator analizuje wszystkie pliki źródłowe i ich zależności
- Parsowanie - przekształca kod źródłowy w Abstract Syntax Tree (AST)
- Sprawdzanie typów - weryfikuje, czy typy są zgodne i wydaje ostrzeżenia/błędy
- Transformacje - przekształca AST do odpowiednich struktur JavaScript
- Emisja - generuje pliki JavaScript, mapy źródłowe i pliki deklaracji typów
Ten proces można porównać do etapów tworzenia dinozaura:
- Skanowanie to zbieranie próbek DNA z różnych źródeł
- Parsowanie to sekwencjonowanie DNA
- Sprawdzanie typów to weryfikacja, czy sekwencje są poprawne i kompatybilne
- Transformacje to modyfikacje genetyczne, aby DNA działało we współczesnym środowisku
- Emisja to umieszczenie gotowego kodu genetycznego w jaju dinozaura
Najważniejsze opcje wiersza poleceń
Kompilator TypeScript oferuje wiele opcji wiersza poleceń, które pozwalają na precyzyjne kontrolowanie procesu kompilacji:
Opcje dotyczące typów plików wyjściowych
1# Określanie katalogu wyjściowego
2tsc --outDir ./dist
3
4# Generowanie plików deklaracji typów (.d.ts)
5tsc --declaration
6
7# Generowanie map źródłowych (.js.map) dla łatwiejszego debugowania
8tsc --sourceMap
9
10# Łączenie wielu plików wyjściowych w jeden (przestarzałe dla modułów ES)
11tsc --outFile ./dist/bundle.jsOpcje kontrolujące proces kompilacji
1# Kompilacja bez emitowania plików (tylko sprawdzenie typów)
2tsc --noEmit
3
4# Kompilacja mimo błędów
5tsc --noEmitOnError false
6
7# Włączenie trybu ścisłego (strict)
8tsc --strict
9
10# Kompilacja w trybie watch (automatyczna rekompilacja po zmianach)
11tsc --watchOpcje wpływające na generowany kod
1# Określanie docelowej wersji języka JavaScript (ES5, ES6, itd.)
2tsc --target ES2015
3
4# Określanie systemu modułów (commonjs, amd, system, esm, itd.)
5tsc --module commonjs
6
7# Zachowywanie struktury katalogów
8tsc --rootDir ./srcOpcje diagnostyczne
1# Wyświetlanie szczegółowych informacji o procesie kompilacji
2tsc --verbose
3
4# Listowanie wszystkich plików, które są częścią kompilacji
5tsc --listFiles
6
7# Wyświetlenie wynikowej konfiguracji
8tsc --showConfigJak TypeScript kompiluje różne konstrukcje kodu?
Zobaczmy, jak kompilator przekształca specyficzne konstrukcje TypeScript na JavaScript:
1. Interfejsy i typy - znikają w trakcie kompilacji
1// Kod TypeScript (security-types.ts)
2interface Employee {
3 id: number;
4 name: string;
5 role: string;
6 accessLevel: number;
7}
8
9type AccessLevel = 1 | 2 | 3 | 4 | 5;
10
11function checkAccess(employee: Employee, requiredLevel: AccessLevel): boolean {
12 return employee.accessLevel >= requiredLevel;
13}1// Skompilowany JavaScript (security-types.js)
2function checkAccess(employee, requiredLevel) {
3 return employee.accessLevel >= requiredLevel;
4}Interfejsy i typy są używane tylko podczas sprawdzania typów i zostają całkowicie usunięte z kodu JavaScript. To jak DNA dinozaura - istotne podczas tworzenia, ale niewidoczne w ostatecznym stworzeniu.
2. Adnotacje typów - również są usuwane
1// Kod TypeScript (employee.ts)
2function createEmployee(name: string, role: string, accessLevel: number): Employee {
3 return {
4 id: generateId(),
5 name,
6 role,
7 accessLevel
8 };
9}
10
11let newEmployee: Employee;1// Skompilowany JavaScript (employee.js)
2function createEmployee(name, role, accessLevel) {
3 return {
4 id: generateId(),
5 name,
6 role,
7 accessLevel
8 };
9}
10
11let newEmployee;Adnotacje typów znikają, ale struktura kodu pozostaje ta sama. To jak mapa genetyczna, która jest używana do tworzenia dinozaura, ale nie jest przenoszona do ostatecznego stworzenia.
3. Zaawansowane funkcje TypeScript, które wymagają transformacji
1// Kod TypeScript (tracking-system.ts)
2// Użycie opcjonalnego łańcuchowania (optional chaining) i operatora nullish coalescing
3function getLocationName(dinosaur?: Dinosaur) {
4 return dinosaur?.currentLocation?.name ?? "Unknown Location";
5}
6
7// Użycie parametrów reszty i destrukturyzacji
8function analyzeDinosaurs(...dinosaurs: Dinosaur[]) {
9 const [mostDangerous, ...others] = dinosaurs.sort((a, b) => b.dangerLevel - a.dangerLevel);
10 return { mostDangerous, others };
11}Kompilacja może wyglądać różnie w zależności od docelowej wersji ES. Dla ES5 może to wyglądać tak:
1// Skompilowany JavaScript (tracking-system.js) dla ES5
2function getLocationName(dinosaur) {
3 var _a, _b;
4 return (_b = (_a = dinosaur === null || dinosaur === void 0 ? void 0 : dinosaur.currentLocation) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : "Unknown Location";
5}
6
7function analyzeDinosaurs() {
8 var dinosaurs = [];
9 for (var _i = 0; _i < arguments.length; _i++) {
10 dinosaurs[_i] = arguments[_i];
11 }
12 var sortedDinosaurs = dinosaurs.sort(function (a, b) { return b.dangerLevel - a.dangerLevel; });
13 var mostDangerous = sortedDinosaurs[0], others = sortedDinosaurs.slice(1);
14 return { mostDangerous: mostDangerous, others: others };
15}Tutaj kompilator musiał wykonać znacznie więcej pracy, aby przekształcić nowoczesne funkcje JavaScript na kod kompatybilny z ES5. To jak dostosowywanie "primitive" kodu genetycznego, aby działał we współczesnym środowisku.
4. Klasy TypeScript
1// Kod TypeScript (dinosaur.ts)
2class Dinosaur {
3 private _healthStatus: number = 100;
4
5 constructor(
6 public readonly species: string,
7 public age: number,
8 private readonly dangerLevel: number
9 ) {}
10
11 get health(): number {
12 return this._healthStatus;
13 }
14
15 feed(amount: number): void {
16 this._healthStatus = Math.min(100, this._healthStatus + amount);
17 console.log(`${this.species} has been fed. New health: ${this._healthStatus}%`);
18 }
19}1// Skompilowany JavaScript (dinosaur.js) dla ES2015
2class Dinosaur {
3 constructor(species, age, dangerLevel) {
4 this.species = species;
5 this.age = age;
6 this.dangerLevel = dangerLevel;
7 this._healthStatus = 100;
8 }
9
10 get health() {
11 return this._healthStatus;
12 }
13
14 feed(amount) {
15 this._healthStatus = Math.min(100, this._healthStatus + amount);
16 console.log(`${this.species} has been fed. New health: ${this._healthStatus}%`);
17 }
18}Dla ES5, klasy będą przekształcone na funkcje konstruktorów i prototypy.
Pliki deklaracji typów (.d.ts)
W laboratorium InGen prowadzi się szczegółowe dokumentacje wszystkich stworzonych gatunków. W świecie TypeScriptu odpowiednikiem takiej dokumentacji są pliki deklaracji typów (.d.ts).
Pliki te zawierają tylko informacje o typach, bez implementacji. Są używane przez IDE i kompilator do zapewnienia podpowiedzi kodu i sprawdzania typów.
1// dinosaur.d.ts - plik deklaracji typów dla klasy Dinosaur
2declare class Dinosaur {
3 readonly species: string;
4 age: number;
5 private readonly dangerLevel: number;
6 private _healthStatus: number;
7
8 constructor(species: string, age: number, dangerLevel: number);
9
10 get health(): number;
11 feed(amount: number): void;
12}Aby wygenerować pliki deklaracji typów dla swojego projektu, możesz użyć opcji --declaration:
1tsc --declarationMapy źródłowe (source maps)
Debugowanie dinozaurów może być skomplikowane - nie zawsze wiemy, której sekwencji DNA odpowiada dane zachowanie. Podobnie, debugowanie skompilowanego kodu JavaScript może być trudne bez map źródłowych.
Mapy źródłowe to pliki .js.map`, które pozwalają przeglądarce lub środowisku Node.js mapować skompilowany kod JavaScript z powrotem na oryginalny kod TypeScript. Dzięki temu możesz debugować bezpośrednio kod TypeScript, nawet gdy uruchamiany jest JavaScript.
Aby wygenerować mapy źródłowe, użyj opcji --sourceMap:
1tsc --sourceMapProjekty (tsconfig.json)
W wielkim parku pełnym różnych gatunków dinozaurów, potrzebujemy szczegółowych planów i specyfikacji. Podobnie, w większych projektach TypeScript używamy pliku tsconfig.json do konfigurowania kompilatora.
Plik ten pozwala na ustawienie wszystkich opcji kompilatora w jednym miejscu, co jest znacznie wygodniejsze niż przekazywanie ich za każdym razem w wierszu poleceń.
1# Utworzenie podstawowego pliku tsconfig.json
2tsc --init
3
4# Kompilacja projektu zgodnie z konfiguracją
5tscChowanie błędów i migracja stopniowa
Dr. Wu czasem musi ignorować pewne niezgodności genetyczne, aby dinozaur mógł przeżyć. Podobnie, podczas migracji istniejącego projektu JavaScript do TypeScript, możemy potrzebować tymczasowo ignorować niektóre błędy typów.
Ignorowanie błędów w konkretnych liniach
1// @ts-ignore
2element.style.background = 'red'; // TypeScript zgłosiłby błąd, ale go ignorujemy
3
4// @ts-expect-error
5console.log(dinosaur.location); // Dokumentujemy, że spodziewamy się tu błęduTymczasowe użycie typu any
1function legacyFunction(data: any) {
2 // Tymczasowo używamy 'any', ale planujemy to poprawić później
3 return data.process();
4}Stopniowe włączanie sprawdzania typów
Możemy również stopniowo włączać sprawdzanie typów w całym projekcie, zaczynając od mniej rygorystycznych ustawień:
1// tsconfig.json z mniej rygorystycznymi ustawieniami
2{
3 "compilerOptions": {
4 "allowJs": true, // Pozwala na kompilację plików .js
5 "checkJs": false, // Nie sprawdza typów w plikach .js
6 "noImplicitAny": false, // Pozwala na niejawny typ 'any'
7 "strictNullChecks": false // Nie wymaga sprawdzania null/undefined
8 }
9}Z czasem, gdy projekt staje się bardziej "udomowiony", możemy stopniowo zwiększać rygorystyczność sprawdzania typów.
Typowe problemy z kompilatorem i ich rozwiązania
1. Konflikt wersji TypeScript
Problem: Globalna instalacja TypeScript może konfliktować z lokalną wersją projektu.
Rozwiązanie: Używaj lokalnie zainstalowanego kompilatora poprzez npx:
1npx tsc2. Niepoprawne ścieżki
Problem: Kompilator nie może znaleźć plików lub kompiluje niewłaściwe pliki.
Rozwiązanie: Sprawdź ustawienia include, exclude, rootDir i outDir w tsconfig.json:
1{
2 "compilerOptions": {
3 "rootDir": "./src",
4 "outDir": "./dist"
5 },
6 "include": ["src/**/*"],
7 "exclude": ["node_modules", "**/*.spec.ts"]
8}3. Niezgodności typów z bibliotekami
Problem: TypeScript zgłasza błędy dotyczące typów w zewnętrznych bibliotekach.
Rozwiązanie: Zainstaluj odpowiednie pliki definicji typów lub utwórz własne:
1npm install --save-dev @types/bibliotekaAlbo utwórz własny plik definicji:
1// types/missing-library.d.ts
2declare module 'missing-library' {
3 export function someFunction(): void;
4}4. Błędy w skomplikowanych typach generycznych
Problem: TypeScript ma problemy z wywnioskowanymi typami w skomplikowanych strukturach generycznych.
Rozwiązanie: Podaj jawne adnotacje typów w strategicznych miejscach:
1// Zamiast pozwalać TypeScript wywnioskować typ:
2const result = complexFunction(data);
3
4// Podaj jawną adnotację typu:
5const result: ExpectedReturnType = complexFunction(data);Zaawansowane użycie: kompilator API
Dla najbardziej zaawansowanych scenariuszy, TypeScript udostępnia API kompilacji, które pozwala na głęboką integrację z narzędziami deweloperskimi. Jest to jak dostęp do zaawansowanych narzędzi laboratorium dla szczególnie wymagających eksperymentów genetycznych.
1import * as ts from 'typescript';
2
3// Tworzymy kompilator host
4const host = ts.createCompilerHost({});
5
6// Tworzymy program TypeScript
7const program = ts.createProgram(['file1.ts', 'file2.ts'], {
8 target: ts.ScriptTarget.ES2015,
9 module: ts.ModuleKind.CommonJS
10}, host);
11
12// Kompilujemy program
13const emitResult = program.emit();
14
15// Pobieramy diagnostykę (błędy i ostrzeżenia)
16const diagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);
17
18// Wyświetlamy diagnostykę
19diagnostics.forEach(diagnostic => {
20 if (diagnostic.file) {
21 const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start!);
22 const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
23 console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
24 } else {
25 console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
26 }
27});Ta funkcjonalność jest używana przez narzędzia takie jak webpack, Rollup czy ESBuild do integracji kompilacji TypeScript w ich własne procesy budowania.
Podsumowanie
Kompilator TypeScript, podobnie jak laboratorium genetyczne InGen, przekształca zaawansowany, bezpieczny kod (TypeScript) w postać, która może być "ożywiona" w przeglądarce lub środowisku Node.js (JavaScript).
Kluczowe punkty:
Kompilator TypeScript (
tsc) przekształca kod TypeScript w JavaScript, usuwając adnotacje typów i przekształcając zaawansowane funkcje.Proces kompilacji obejmuje: skanowanie, parsowanie, sprawdzanie typów, transformacje i emisję.
Istnieje wiele opcji kompilatora, które można przekazać w linii poleceń lub skonfigurować w pliku
tsconfig.json.Każdy TypeScript feature ma sposób kompilacji do JavaScript, przy czym złożoność tej kompilacji zależy od docelowej wersji JavaScript.
Pliki
.d.tsi mapy źródłowe.js.mappomagają w dokumentacji i debugowaniu.W przypadku migracji istniejących projektów, TypeScript pozwala na stopniowe wprowadzanie typów.
Dla zaawansowanych scenariuszy, istnieje API kompilacji TypeScript.
Tak jak laboratorium InGen potrzebuje precyzyjnych protokołów i narzędzi do tworzenia dinozaurów, tak programiści potrzebują dobrze skonfigurowanego kompilatora TypeScript, aby tworzyć bezpieczne i wydajne aplikacje JavaScript.
W następnej lekcji zagłębimy się w podstawowe typy dostępne w TypeScript - to będzie jak studiowanie różnych gatunków dinozaurów w naszym parku!
Kod do tej lekcji: index.ts
1// TypeScript Compiler - Laboratorium InGen
2console.log("Kompilator TypeScript w akcji!\n");
3
4// ===========================================
5// 1. Podstawy kompilacji
6// ===========================================
7console.log("=== PODSTAWY KOMPILACJI ===");
8console.log("TypeScript -> tsc -> JavaScript\n");
9
10// Ten kod TypeScript zostanie skompilowany do JavaScript
11interface Dinosaur {
12 species: string;
13 period: 'Triassic' | 'Jurassic' | 'Cretaceous';
14 diet: 'carnivore' | 'herbivore' | 'omnivore';
15 weight: number;
16}
17
18const trex: Dinosaur = {
19 species: "Tyrannosaurus Rex",
20 period: 'Cretaceous',
21 diet: 'carnivore',
22 weight: 8000
23};
24
25console.log("Dinosaur data:", trex);
26
27// ===========================================
28// 2. Kompilacja z sprawdzaniem typów
29// ===========================================
30console.log("\n=== SPRAWDZANIE TYPÓW ===");
31
32function calculateFeedAmount(dino: Dinosaur): number {
33 // Drapieżniki jedzą 5% swojej wagi dziennie
34 // Roślinożercy jedzą 10% swojej wagi dziennie
35 const percentage = dino.diet === 'carnivore' ? 0.05 : 0.10;
36 return dino.weight * percentage;
37}
38
39const feedAmount = calculateFeedAmount(trex);
40console.log(`Daily feed for ${trex.species}: ${feedAmount}kg`);
41
42// ===========================================
43// 3. Target ES version
44// ===========================================
45console.log("\n=== TARGET ES VERSION ===");
46
47// Arrow functions (ES6+)
48const getDinosaurs = (): Dinosaur[] => [trex];
49
50// Template literals (ES6+)
51const description = `${trex.species} lived in ${trex.period} period`;
52console.log(description);
53
54// Optional chaining (ES2020+)
55interface Park {
56 dinosaurs?: Dinosaur[];
57}
58
59const park: Park = { dinosaurs: getDinosaurs() };
60const firstDino = park.dinosaurs?.[0];
61console.log("First dinosaur:", firstDino?.species);
62
63// ===========================================
64// 4. Moduły
65// ===========================================
66console.log("\n=== MODUŁY ===");
67
68// Export/Import (ES6 modules)
69export class DinosaurManager {
70 private dinosaurs: Dinosaur[] = [];
71
72 add(dino: Dinosaur): void {
73 this.dinosaurs.push(dino);
74 console.log(`Added ${dino.species} to collection`);
75 }
76
77 getAll(): Dinosaur[] {
78 return [...this.dinosaurs];
79 }
80
81 findByDiet(diet: Dinosaur['diet']): Dinosaur[] {
82 return this.dinosaurs.filter(d => d.diet === diet);
83 }
84}
85
86const manager = new DinosaurManager();
87manager.add(trex);
88console.log("Total dinosaurs:", manager.getAll().length);
89
90// ===========================================
91// 5. Generowanie plików
92// ===========================================
93console.log("\n=== PLIKI WYGENEROWANE ===");
94console.log("JavaScript (.js) - wykonywalny kod");
95console.log("Source Maps (.js.map) - mapowanie do TS");
96console.log("Declaration Files (.d.ts) - definicje typów");
97
98console.log("\nKompilator TypeScript gotowy do pracy!");Widzisz błąd w tej lekcji?
Sprawdź się
Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.
1. Jakie polecenie uruchamia kompilator TypeScript?
2. Co robi opcja 'strict: true' w pliku tsconfig.json?
Zadania praktyczne w grze
- Edytor kodu
Zdefiniuj funkcję z typami parametrów i sprawdź błędy kompilacji.
- Klikanie w kolejności
Ułóż polecenie kompilacji TypeScript z katalogiem wyjściowym:
- Układanie w pionie
Uporządkuj etapy procesu kompilacji TypeScript: