Руководство и документация по языку программирования TypeScript

Фундаментальный разбор TypeScript: архитектура транспиляции, статическая типизация, интерфейсы, Generic-типы, утилитарные типы, метапрограммирование и оптимизация веб-приложений.

Важное примечание по версиям и компилятору: TypeScript строго эволюционирует вместе со стандартами ECMAScript. Различные версии компилятора (tsc) добавляют новые проверки строгости в tsconfig.json (например, exactOptionalPropertyTypes или noUncheckedIndexedAccess). Рекомендуется всегда использовать флаг "strict": true и сверять специфичные системные типы с официальной документацией TypeScript Handbook.

1. Экосистема TypeScript и архитектура транспиляции

TypeScript — это строго типизированный надмножество (superset) языка JavaScript, разработанный для масштабируемой веб-разработки. Главная особенность TypeScript заключается в том, что он существует исключительно на этапе разработки (Compile-Time). Вся система типов полностью удаляется (Type Erasure) в процессе компиляции, превращая исходный код в чистый JavaScript, исполняемый в браузере, Node.js, Bun или Deno.

Компилятор TypeScript (tsc) осуществляет комплексный процесс статическго анализа: синтаксический разбор (AST), синтаксическую валидацию, сопоставление графа типов и выведение типов (Type Inference). Это позволяет отлавливать категории ошибок (null pointer exceptions, обращения к несуществующим полям, неверная сигнатура функций) еще до запуска приложения.

2. Система типов: Примитивы, Type vs Interface и Literal Types

TypeScript расширяет базовые типы JavaScript (string, number, boolean, bigint, symbol) собственными специализированными конструктами. В отличие от C#, где применяется номинативная типизация (Nominal Typing), TypeScript полностью построен на структурной типизации (Structural Typing / Duck Typing). Это означает, что два типа совместимы, если они имеют одинаковую форму и структуру, независимо от их имени.

Ниже представлен пример описания структуры пользователя и конфига с синтаксической подсветкой:

export type Role = 'admin' | 'moderator' | 'user';

export interface BaseEntity {
    readonly id: number;
    createdAt: Date;
}

export interface UserProfile extends BaseEntity {
    username: string;
    email: string;
    role: Role;
    bio?: string;
}

export class UserAccount implements UserProfile {
    public readonly id: number;
    public createdAt: Date;
    public username: string;
    public email: string;
    public role: Role;

    constructor(id: number, username: string, email: string, role: Role = 'user') {
        this.id = id;
        this.createdAt = new Date();
        this.username = username;
        this.email = email;
        this.role = role;
    }

    public getDisplayInfo(): string {
        return `[${this.role.toUpperCase()}] ${this.username} (${this.email})`;
    }
}
Разбор синтаксиса: ключевое слово readonly блокирует возможность мутации свойства после инициализации. Знак ? указывает на опциональное свойство, автоматически добавляющее к типу | undefined.

3. Обобщенное программирование (Generics) и ограничения

Дженерики (Generics) в TypeScript позволяют создавать переиспользуемые компоненты, абстрагированные от конкретных типов данных, сохраняя при этом строгую типизацию. Это ключевой инструмент при написании оберток над сетевыми запросами, хранилищами, коллекциями и шаблонами проектирования.

С помощью оператора extends можно накладывать ограничения на обобщенные параметры (Generic Constraints), гарантируя наличие определенных полей или методов у передаваемого типа.

export interface ApiResponse<TData> {
    status: 200 | 400 | 404 | 500;
    success: boolean;
    payload: TData;
    timestamp: number;
}

export interface Identifiable {
    id: number | string;
}

export class DataRepository<T extends Identifiable> {
    private items: Map<number | string, T> = new Map();

    public save(item: T): void {
        this.items.set(item.id, item);
    }

    public findById(id: number | string): T | undefined {
        return this.items.get(id);
    }

    public getAll(): T[] {
        return Array.from(this.items.values());
    }
}

4. Продвинутая типизация: Mapped Types, Conditional Types и Utility Types

TypeScript содержит полноценный Тьюринг-полный язык типов на этапе компиляции. Он позволяет трансформировать существующие типы, вычислять новые и автоматически синхронизировать структуры данных.

Сопоставимые типы (Mapped Types) и Условные типы (Conditional Types) лежат в основе большинства встроенных утилитарных типов компилятора (Utility Types):

// Встроенные системные Utility Types
type ReadonlyUser = Readonly<UserProfile>;
type CreateUserDTO = Omit<UserProfile, 'id' | 'createdAt'>;
type PartialUserConfig = Partial<UserProfile>;
type UserCredentials = Pick<UserProfile, 'username' | 'email'>;

// Кастомный Mapped Type: делает все поля типа вычисляемыми или Nullable
type Nullable<T> = {
    [K in keyof T]: T[K] | null;
};

// Conditional Type со словосочетанием infer для извлечения возвращаемого типа функции
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;

5. Сужение типов (Type Narrowing), Type Guards и асинхронность

Так как TypeScript компилируется в JavaScript, при обработке неопределенных внешних данных (например, ответа от API или событий ввода) необходимы механизмы контроля типов в Runtime. TypeScript анализирует поток выполнения кода (Control Flow Analysis) и сужает типы внутри логических блоков if, switch или try/catch.

Для создания кастомных проверок используются **Type Predicates** (Пользовательские защитники типов):

export interface SuccessState {
    status: 'success';
    data: string[];
}

export interface ErrorState {
    status: 'error';
    error: Error;
}

export type NetworkState = SuccessState | ErrorState;

// Custom Type Guard (Предикат типа)
export function isSuccessState(state: NetworkState): state is SuccessState {
    return state.status === 'success';
}

export async function handleNetworkRequest(url: string): Promise<void> {
    try {
        const response = await fetch(url);
        const payload: NetworkState = await response.json();

        if (isSuccessState(payload)) {
            // Внутри этого блока payload автоматически сужен до SuccessState
            Console.log(`Успешно получено элементов: ${payload.data.length}`);
        } else {
            // Здесь payload автоматически становится ErrorState
            Console.error(`Ошибка запроса: ${payload.error.message}`);
        }
    } catch (err) {
        Console.error('Сетевая ошибка', err);
    }
}

6. Конфигурация tsconfig.json, оптимизация и метапрограммирование

Поведение компилятора TypeScript полностью управляется файлом tsconfig.json. Он определяет правила транспиляции, целевую версию ECMAScript, модульную систему и степень строгости проверок типов.

Параметр tsconfig Значение Назначение и эффект на сборку
target ES2022 / ESNext Задает версию синтаксиса JavaScript, в которую скомпилируется итоговый код.
moduleResolution NodeNext / Bundler Определяет алгоритм поиска и резолвинга модулей и пакетов node_modules.
strict true Включает полный комплекс строгих проверок (noImplicitAny, strictNullChecks и др.).
noUnusedLocals true Вызывает ошибку компиляции при наличии неиспользуемых локальных переменных.
skipLibCheck true Пропускает виртуальную проверку типов файлов деклараций (.d.ts) для ускорения сборки.

Пример применения встроенных декораторов (Stage 3 Decorators / Experimental Decorators) для логирования и метапрограммирования:

function LogMethodExecution(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;

    descriptor.value = async function (...args: any[]) {
        Console.log(`[LOG] Вызов метода ${propertyKey} с аргументами:`, args);
        const result = await originalMethod.apply(this, args);
        Console.log(`[LOG] Метод ${propertyKey} завершен с результатом:`, result);
        return result;
    };

    return descriptor;
}

export class UserService {
    @LogMethodExecution
    public async getUserById(id: number): Promise<UserProfile> {
        return new UserAccount(id, "Muras", "muras@fexcharged.com", "admin");
    }
}