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.
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:
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 typescriptKompilowanie 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.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 TypeScript przebiega w kilku krokach, podobnie jak proces tworzenia dinozaura w laboratorium:
Ten proces można porównać do etapów tworzenia dinozaura:
Kompilator TypeScript oferuje wiele opcji wiersza poleceń, które pozwalają na precyzyjne kontrolowanie procesu kompilacji:
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.js1# 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 --watch1# 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 ./src1# 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 --showConfigZobaczmy, jak kompilator przekształca specyficzne konstrukcje TypeScript na JavaScript:
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.
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.
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.
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.
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 --declarationDebugowanie 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 --sourceMapW 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ą
5tscDr. 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.
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łęduany1function legacyFunction(data: any) {
2 // Tymczasowo używamy 'any', ale planujemy to poprawić później
3 return data.process();
4}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.
Problem: Globalna instalacja TypeScript może konfliktować z lokalną wersją projektu.
Rozwiązanie: Używaj lokalnie zainstalowanego kompilatora poprzez npx:
1npx tscProblem: 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}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}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);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, '
23');
24 console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
25 } else {
26 console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, '
27'));
28 }
29});Ta funkcjonalność jest używana przez narzędzia takie jak webpack, Rollup czy ESBuild do integracji kompilacji TypeScript w ich własne procesy budowania.
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.map pomagają 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!