CS

Vývoj v TypeScriptu

TypeScript Development Guide

This comprehensive guide covers TypeScript programming, including core concepts, advanced features, tooling, and best practices for modern web development.

Getting Started with TypeScript

Installation and Setup

Global Installation:

npm install -g typescript

Project Setup:

# Initialize npm project
npm init -y

# Install TypeScript locally
npm install --save-dev typescript

# Initialize TypeScript configuration
npx tsc --init

Basic 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"]
}

TypeScript Fundamentals

Basic Types

Primitive Types:

// String, number, boolean
let name: string = "John";
let age: number = 30;
let isStudent: boolean = false;

// Array types
let numbers: number[] = [1, 2, 3, 4];
let names: Array<string> = ["Alice", "Bob"];

// Tuple (fixed-length array)
let person: [string, number] = ["John", 30];

// Enum
enum Color {
  Red,
  Green,
  Blue
}
let color: Color = Color.Red;

// Any (avoid when possible)
let anything: any = "could be anything";

// Void (for functions that don't return)
function logMessage(): void {
  console.log("Hello!");
}

// Never (for functions that never return)
function throwError(message: string): never {
  throw new Error(message);
}

Interfaces and Type Aliases

Interfaces:

interface User {
  id: number;
  name: string;
  email: string;
  isActive?: boolean;  // Optional property
  readonly createdAt: Date;  // Read-only property
}

// Extending interfaces
interface Admin extends User {
  role: string;
  permissions: string[];
}

// Interface for functions
interface Comparator<T> {
  (a: T, b: T): number;
}

// Interface for indexable types
interface Dictionary<T> {
  [key: string]: T;
}

Type Aliases:

// Simple type alias
type UserId = number;

// Union types
type Status = "active" | "inactive" | "pending";

// Intersection types
type AdminUser = User & { role: string };

// Generic type alias
type ApiResponse<T> = {
  data: T;
  status: number;
  message: string;
};

Classes

Basic Class:

class Person {
  // Properties
  public name: string;
  private age: number;
  protected email: string;

  // Constructor
  constructor(name: string, age: number, email: string) {
    this.name = name;
    this.age = age;
    this.email = email;
  }

  // Methods
  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;
  }
}

// Usage
const person = new Person("John", 30, "john@example.com");
console.log(person.greet());

Inheritance:

class Employee extends Person {
  private salary: number;

  constructor(name: string, age: number, email: string, salary: number) {
    super(name, age, email);
    this.salary = salary;
  }

  // Override method
  greet(): string {
    return `Hello, I'm ${this.name} and I work here.`;
  }

  // New method
  getSalary(): number {
    return this.salary;
  }
}

Abstract Classes:

abstract class Shape {
  abstract getArea(): number;
  abstract getPerimeter(): number;

  // Concrete method
  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);
  }
}

Generics

Generic Functions:

function identity<T>(arg: T): T {
  return arg;
}

// Usage
let output1 = identity<string>("Hello");
let output2 = identity<number>(42);

// Multiple type parameters
function combine<T, U>(first: T, second: U): [T, U] {
  return [first, second];
}

Generic Classes:

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;
  }
}

// Usage
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
console.log(numberStack.pop()); // 2

Generic Constraints:

interface Lengthwise {
  length: number;
}

function logLength<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);
  return arg;
}

// Usage
logLength("Hello");      // OK
logLength([1, 2, 3]);    // OK
logLength(42);           // Error: number has no length property

Advanced TypeScript Features

Union and Intersection Types

Union Types:

type StringOrNumber = string | number;

function formatValue(value: StringOrNumber): string {
  if (typeof value === "string") {
    return value.toUpperCase();
  } else {
    return value.toFixed(2);
  }
}

// Discriminated unions
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 Types:

type Timestamped = { timestamp: Date };
type Named = { name: string };

type TimestampedNamed = Timestamped & Named;

// Equivalent to:
// type TimestampedNamed = {
//   timestamp: Date;
//   name: string;
// }

Type Guards and Assertions

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);
  }
}

Type Assertions:

// Angle-bracket syntax
let someValue: unknown = "hello world";
let strLength: number = (<string>someValue).length;

// as syntax (recommended)
let strLength2: number = (someValue as string).length;

// Non-null assertion
function process(user: User | null) {
  console.log(user!.name);  // Asserts user is not null
}

Utility Types

Built-in Utility Types:

interface Todo {
  id: number;
  title: string;
  completed: boolean;
  createdAt: Date;
}

// Partial - makes all properties optional
type PartialTodo = Partial<Todo>;

// Required - makes all properties required
type RequiredTodo = Required<Todo>;

// Pick - selects specific properties
type TodoPreview = Pick<Todo, "id" | "title">;

// Omit - removes specific properties
type TodoWithoutId = Omit<Todo, "id">;

// Readonly - makes all properties readonly
type ReadonlyTodo = Readonly<Todo>;

Custom Utility Types:

// Extract function return type
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;

// Extract promise resolved type
type Awaited<T> = T extends PromiseLike<infer U> ? U : T;

// Make specific properties optional
type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;

TypeScript Compiler (tsc) Commands

Basic Compilation

Compile all files:

npx tsc

Compile specific file:

npx tsc file.ts

Initialize TypeScript project:

npx tsc --init

Watch Mode and Output

Watch mode (continuous compilation):

npx tsc --watch
npx tsc -w

Specify output directory:

npx tsc --outDir ./dist

Specify root directory:

npx tsc --rootDir ./src

Compilation Options

Target ECMAScript version:

npx tsc --target ES2020
npx tsc --target ES5

Module system:

npx tsc --module commonjs
npx tsc --module es2020

Include/exclude files:

npx tsc --include src/**/*.ts
npx tsc --exclude node_modules

Type Checking and Diagnostics

Strict type checking:

npx tsc --strict

Type check without emitting files:

npx tsc --noEmit

Generate declaration files:

npx tsc --declaration

Generate source maps:

npx tsc --sourceMap

Project Configuration

Use specific tsconfig.json:

npx tsc --project ./path/to/tsconfig.json
npx tsc -p ./path/to/tsconfig.json

Incremental compilation:

npx tsc --incremental

Remove comments from output:

npx tsc --removeComments

Module Systems

ES6 Modules

Export:

// Named exports
export const PI = 3.14159;
export function calculateArea(radius: number): number {
  return PI * radius * radius;
}

// Default export
export default class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
}

Import:

// Named imports
import { PI, calculateArea } from './math';

// Default import
import Calculator from './calculator';

// Rename imports
import { calculateArea as area } from './math';

// Import all
import * as MathUtils from './math';

CommonJS (Node.js)

Export:

// Single export
export = function() {
  return "Hello";
};

// Or using module.exports
const utils = {
  add: (a: number, b: number) => a + b,
  multiply: (a: number, b: number) => a * b
};

export = utils;

Import:

// Import CommonJS module
import utils = require('./utils');

Decorators

Class Decorators:

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;
  }
}

Method Decorators:

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;
  }
}

Error Handling and Debugging

Error Types

Custom Error Classes:

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';
  }
}

Error Handling Patterns:

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;
  }
}

Testing with TypeScript

Using Jest

Setup:

npm install --save-dev jest @types/jest ts-jest
npx ts-jest config:init

Basic 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);
  });
});

Test Configuration (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',
  ],
};

Best Practices

Code Organization

  1. File Structure:

    src/
    ├── types/
    ├── interfaces/
    ├── utils/
    ├── services/
    ├── components/
    └── index.ts
    
  2. Naming Conventions:

    • Use PascalCase for classes, interfaces, and type aliases
    • Use camelCase for variables, functions, and properties
    • Use UPPER_CASE for constants
  3. Type Safety:

    • Avoid any type when possible
    • Use strict mode in tsconfig.json
    • Leverage union types for better type safety

Performance Considerations

  1. Tree Shaking: Use ES6 modules for better bundling

  2. Type-Only Imports: Import types without runtime overhead

    import type { User } from './types';
    
  3. Declaration Files: Generate .d.ts files for libraries

Tooling and Development

ESLint Configuration:

{
  "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"
  }
}

Prettier Configuration:

{
  "semi": true,
  "trailingComma": "es5",
  "singleQuote": true,
  "printWidth": 80,
  "tabWidth": 2
}

Learning Resources

This guide provides a comprehensive foundation for TypeScript development, covering core concepts, advanced features, tooling, and best practices for building robust applications.