TypeScript
Co tato dovednost pokrývá
Praktický referenční přehled vývoje v TypeScriptu: nastavení projektu a tsconfig.json, základní i pokročilý typový systém (interfacy, generika, uniony, utility types), CLI kompilátoru tsc, modulové systémy, dekorátory, zpracování chyb, testování s Jestem a osvědčené postupy pro strukturu projektu.
Kdy ji použít
- Zakládání nového TypeScript projektu nebo psaní
tsconfig.jsonod nuly - Rozhodování mezi interfacem, type aliasem nebo generikem pro daný kus kódu
- Vyhledávání přepínačů kompilátoru
tsc— watch mode, strict mode, cílový výstup/modul, deklarační soubory - Konfigurace ES modulů vs. CommonJS interoperability v TypeScript projektu
- Práce s type guards, typovými asercemi nebo utility types jako
Partial,Pick,Omit - Psaní vlastních dekorátorů pro třídy nebo metody
- Nastavení Jestu s
ts-jestpro testování TypeScript kódu - Strukturování TypeScript projektu, konvence pojmenování nebo konfigurace ESLintu/Prettieru
Začínáme s TypeScriptem
Instalace a nastavení
Globální instalace:
npm install -g typescript
Nastavení projektu:
# Inicializace npm projektu
npm init -y
# Lokální instalace TypeScriptu
npm install --save-dev typescript
# Inicializace konfigurace TypeScriptu
npx tsc --init
Základní tsconfig.json:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}
Základy TypeScriptu
Základní typy
Primitivní typy:
// String, number, boolean
let name: string = "John";
let age: number = 30;
let isStudent: boolean = false;
// Typy polí
let numbers: number[] = [1, 2, 3, 4];
let names: Array<string> = ["Alice", "Bob"];
// Tuple (pole pevné délky)
let person: [string, number] = ["John", 30];
// Enum
enum Color {
Red,
Green,
Blue
}
let color: Color = Color.Red;
// Any (vyhýbat se, pokud možno)
let anything: any = "could be anything";
// Void (pro funkce bez návratové hodnoty)
function logMessage(): void {
console.log("Hello!");
}
// Never (pro funkce, které nikdy nevrátí hodnotu)
function throwError(message: string): never {
throw new Error(message);
}
Interfacy a typové aliasy
Interfacy:
interface User {
id: number;
name: string;
email: string;
isActive?: boolean; // Volitelná vlastnost
readonly createdAt: Date; // Vlastnost pouze pro čtení
}
// Rozšíření interfaců
interface Admin extends User {
role: string;
permissions: string[];
}
// Interface pro funkce
interface Comparator<T> {
(a: T, b: T): number;
}
// Interface pro indexovatelné typy
interface Dictionary<T> {
[key: string]: T;
}
Typové aliasy:
// Jednoduchý typový alias
type UserId = number;
// Union typy
type Status = "active" | "inactive" | "pending";
// Průnikové (intersection) typy
type AdminUser = User & { role: string };
// Generický typový alias
type ApiResponse<T> = {
data: T;
status: number;
message: string;
};
Třídy
Základní třída:
class Person {
// Vlastnosti
public name: string;
private age: number;
protected email: string;
// Konstruktor
constructor(name: string, age: number, email: string) {
this.name = name;
this.age = age;
this.email = email;
}
// Metody
public greet(): string {
return `Hello, my name is ${this.name}`;
}
// Getter
get fullInfo(): string {
return `${this.name} (${this.age} years old)`;
}
// Setter
set updateEmail(newEmail: string) {
this.email = newEmail;
}
}
// Použití
const person = new Person("John", 30, "john@example.com");
console.log(person.greet());
Dědičnost:
class Employee extends Person {
private salary: number;
constructor(name: string, age: number, email: string, salary: number) {
super(name, age, email);
this.salary = salary;
}
// Přepsání metody
greet(): string {
return `Hello, I'm ${this.name} and I work here.`;
}
// Nová metoda
getSalary(): number {
return this.salary;
}
}
Abstraktní třídy:
abstract class Shape {
abstract getArea(): number;
abstract getPerimeter(): number;
// Konkrétní metoda
describe(): string {
return `Area: ${this.getArea()}, Perimeter: ${this.getPerimeter()}`;
}
}
class Rectangle extends Shape {
constructor(private width: number, private height: number) {
super();
}
getArea(): number {
return this.width * this.height;
}
getPerimeter(): number {
return 2 * (this.width + this.height);
}
}
Generika
Generické funkce:
function identity<T>(arg: T): T {
return arg;
}
// Použití
let output1 = identity<string>("Hello");
let output2 = identity<number>(42);
// Více typových parametrů
function combine<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
Generické třídy:
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
isEmpty(): boolean {
return this.items.length === 0;
}
}
// Použití
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2
Generická omezení (constraints):
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
// Použití
logLength("Hello"); // OK
logLength([1, 2, 3]); // OK
logLength(42); // Chyba: number nemá vlastnost length
Pokročilé funkce TypeScriptu
Union a intersection typy
Union typy:
type StringOrNumber = string | number;
function formatValue(value: StringOrNumber): string {
if (typeof value === "string") {
return value.toUpperCase();
} else {
return value.toFixed(2);
}
}
// Diskriminované uniony
type SuccessResponse = { success: true; data: any };
type ErrorResponse = { success: false; error: string };
type ApiResponse = SuccessResponse | ErrorResponse;
function handleResponse(response: ApiResponse) {
if (response.success) {
console.log("Data:", response.data);
} else {
console.error("Error:", response.error);
}
}
Intersection typy:
type Timestamped = { timestamp: Date };
type Named = { name: string };
type TimestampedNamed = Timestamped & Named;
// Ekvivalentní:
// type TimestampedNamed = {
// timestamp: Date;
// name: string;
// }
Type guards a asercí
Type guards:
function isString(value: unknown): value is string {
return typeof value === "string";
}
function isNumber(value: unknown): value is number {
return typeof value === "number";
}
function processValue(value: unknown) {
if (isString(value)) {
console.log("String length:", value.length);
} else if (isNumber(value)) {
console.log("Number squared:", value * value);
}
}
Typové asercí:
// Syntaxe s lomenými závorkami
let someValue: unknown = "hello world";
let strLength: number = (<string>someValue).length;
// Syntaxe as (doporučeno)
let strLength2: number = (someValue as string).length;
// Non-null asercí
function process(user: User | null) {
console.log(user!.name); // Tvrdí, že user není null
}
Utility types
Vestavěné utility types:
interface Todo {
id: number;
title: string;
completed: boolean;
createdAt: Date;
}
// Partial - všechny vlastnosti volitelné
type PartialTodo = Partial<Todo>;
// Required - všechny vlastnosti povinné
type RequiredTodo = Required<Todo>;
// Pick - vybere konkrétní vlastnosti
type TodoPreview = Pick<Todo, "id" | "title">;
// Omit - odstraní konkrétní vlastnosti
type TodoWithoutId = Omit<Todo, "id">;
// Readonly - všechny vlastnosti pouze pro čtení
type ReadonlyTodo = Readonly<Todo>;
Vlastní utility types:
// Extrakce návratového typu funkce
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;
// Extrakce typu, na který se rozřeší promise
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;
// Konkrétní vlastnosti nastavené jako volitelné
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
Příkazy kompilátoru TypeScriptu (tsc)
Základní kompilace
Kompilace všech souborů:
npx tsc
Kompilace konkrétního souboru:
npx tsc file.ts
Inicializace TypeScript projektu:
npx tsc --init
Watch mode a výstup
Watch mode (průběžná kompilace):
npx tsc --watch
npx tsc -w
Určení výstupního adresáře:
npx tsc --outDir ./dist
Určení kořenového adresáře:
npx tsc --rootDir ./src
Možnosti kompilace
Cílová verze ECMAScriptu:
npx tsc --target ES2020
npx tsc --target ES5
Modulový systém:
npx tsc --module commonjs
npx tsc --module es2020
Zahrnutí/vyloučení souborů:
npx tsc --include src/**/*.ts
npx tsc --exclude node_modules
Kontrola typů a diagnostika
Přísná kontrola typů:
npx tsc --strict
Kontrola typů bez generování souborů:
npx tsc --noEmit
Generování deklaračních souborů:
npx tsc --declaration
Generování source maps:
npx tsc --sourceMap
Konfigurace projektu
Použití konkrétního tsconfig.json:
npx tsc --project ./path/to/tsconfig.json
npx tsc -p ./path/to/tsconfig.json
Inkrementální kompilace:
npx tsc --incremental
Odstranění komentářů z výstupu:
npx tsc --removeComments
Modulové systémy
ES6 moduly
Export:
// Pojmenované exporty
export const PI = 3.14159;
export function calculateArea(radius: number): number {
return PI * radius * radius;
}
// Výchozí export
export default class Calculator {
add(a: number, b: number): number {
return a + b;
}
}
Import:
// Pojmenované importy
import { PI, calculateArea } from './math';
// Výchozí import
import Calculator from './calculator';
// Přejmenování importů
import { calculateArea as area } from './math';
// Import všeho
import * as MathUtils from './math';
CommonJS (Node.js)
Export:
// Jediný export
export = function() {
return "Hello";
};
// Nebo pomocí module.exports
const utils = {
add: (a: number, b: number) => a + b,
multiply: (a: number, b: number) => a * b
};
export = utils;
Import:
// Import CommonJS modulu
import utils = require('./utils');
Dekorátory
Níže uvedená legacy syntaxe dekorátorů vyžaduje "experimentalDecorators": true v tsconfig.json (v základní konfiguraci výše není nastaveno). TypeScript 5+ navíc nabízí samostatnou, standardizovanou syntaxi dekorátorů s mírně odlišnou sémantikou — před použitím těchto příkladů ověřte, kterou variantu projekt cílí.
Dekorátory tříd:
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
greet() {
return "Hello, " + this.greeting;
}
}
Dekorátory metod:
function enumerable(value: boolean) {
return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
descriptor.enumerable = value;
};
}
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
@enumerable(false)
greet() {
return "Hello, " + this.greeting;
}
}
Zpracování chyb a ladění
Typy chyb
Vlastní třídy chyb:
class ValidationError extends Error {
constructor(message: string, public field: string) {
super(message);
this.name = 'ValidationError';
}
}
class NetworkError extends Error {
constructor(message: string, public statusCode: number) {
super(message);
this.name = 'NetworkError';
}
}
Vzory pro zpracování chyb:
async function fetchData(url: string): Promise<any> {
try {
const response = await fetch(url);
if (!response.ok) {
throw new NetworkError('Network request failed', response.status);
}
return await response.json();
} catch (error) {
if (error instanceof NetworkError) {
console.error(`Network error ${error.statusCode}: ${error.message}`);
} else if (error instanceof ValidationError) {
console.error(`Validation error for ${error.field}: ${error.message}`);
} else {
console.error('Unknown error:', error);
}
throw error;
}
}
Testování v TypeScriptu
Použití Jestu
Nastavení:
npm install --save-dev jest @types/jest ts-jest
npx ts-jest config:init
Základní test:
// sum.ts
export function sum(a: number, b: number): number {
return a + b;
}
// sum.test.ts
import { sum } from './sum';
describe('sum', () => {
it('should add two numbers', () => {
expect(sum(1, 2)).toBe(3);
});
it('should handle negative numbers', () => {
expect(sum(-1, 1)).toBe(0);
});
});
Konfigurace testů (jest.config.js)
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/src', '<rootDir>/tests'],
testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
],
};
Osvědčené postupy
Organizace kódu
Struktura souborů:
src/ ├── types/ ├── interfaces/ ├── utils/ ├── services/ ├── components/ └── index.tsKonvence pojmenování:
- PascalCase pro třídy, interfacy a typové aliasy
- camelCase pro proměnné, funkce a vlastnosti
- UPPER_CASE pro konstanty
Typová bezpečnost:
- Vyhýbat se typu
any, pokud je to možné - Používat strict mode v tsconfig.json
- Využívat union typy pro lepší typovou bezpečnost
- Vyhýbat se typu
Výkonnostní ohledy
- Tree shaking: Používat ES6 moduly pro lepší bundling
- Importy pouze typů: Import typů bez dopadu na runtime
import type { User } from './types'; - Deklarační soubory: Generovat .d.ts soubory pro knihovny
Nástroje a vývoj
Konfigurace ESLintu:
{
"extends": [
"eslint:recommended",
"@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/no-unused-vars": "error",
"@typescript-eslint/explicit-function-return-type": "warn"
}
}
Konfigurace Prettieru:
{
"semi": true,
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80,
"tabWidth": 2
}
Studijní zdroje
- Oficiální dokumentace: https://www.typescriptlang.org/docs/
- TypeScript Handbook: https://www.typescriptlang.org/docs/handbook/intro.html
- Microsoft Learn: https://learn.microsoft.com/en-us/training/browse/?terms=typescript
- TypeScript Deep Dive: https://basarat.gitbook.io/typescript/