Używamy cookies, żeby zwiększyć Twoje doświadczenia na stronie
CodeWorlds

Kompilator TypeScript (tsc)

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 typescript

Kompilacja pojedynczego pliku

Kompilowanie pojedynczego pliku TypeScript to jak tworzenie pojedynczego dinozaura w laboratorium:

1# Składnia: tsc [nazwa-pliku].ts
2tsc security-system.ts

Po 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 --watch

Proces kompilacji

Proces kompilacji TypeScript przebiega w kilku krokach, podobnie jak proces tworzenia dinozaura w laboratorium:

  1. Skanowanie - kompilator analizuje wszystkie pliki źródłowe i ich zależności
  2. Parsowanie - przekształca kod źródłowy w Abstract Syntax Tree (AST)
  3. Sprawdzanie typów - weryfikuje, czy typy są zgodne i wydaje ostrzeżenia/błędy
  4. Transformacje - przekształca AST do odpowiednich struktur JavaScript
  5. Emisja - generuje pliki JavaScript, mapy źródłowe i pliki deklaracji typów

Ten proces można porównać do etapów tworzenia dinozaura:

  1. Skanowanie to zbieranie próbek DNA z różnych źródeł
  2. Parsowanie to sekwencjonowanie DNA
  3. Sprawdzanie typów to weryfikacja, czy sekwencje są poprawne i kompatybilne
  4. Transformacje to modyfikacje genetyczne, aby DNA działało we współczesnym środowisku
  5. 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.js

Opcje 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 --watch

Opcje 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 ./src

Opcje 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 --showConfig

Jak 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 --declaration

Mapy ź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 --sourceMap

Projekty (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ą
5tsc

Chowanie 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łędu

Tymczasowe 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 tsc

2. 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/biblioteka

Albo 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, '
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.

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:

  1. Kompilator TypeScript (

    tsc
    ) przekształca kod TypeScript w JavaScript, usuwając adnotacje typów i przekształcając zaawansowane funkcje.

  2. Proces kompilacji obejmuje: skanowanie, parsowanie, sprawdzanie typów, transformacje i emisję.

  3. Istnieje wiele opcji kompilatora, które można przekazać w linii poleceń lub skonfigurować w pliku

    tsconfig.json
    .

  4. Każdy TypeScript feature ma sposób kompilacji do JavaScript, przy czym złożoność tej kompilacji zależy od docelowej wersji JavaScript.

  5. Pliki

    .
    d.ts
    i mapy źródłowe
    .
    js.map
    pomagają w dokumentacji i debugowaniu.

  6. W przypadku migracji istniejących projektów, TypeScript pozwala na stopniowe wprowadzanie typów.

  7. 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!

Przejdź do CodeWorlds