Руководство и документация по языку программирования TypeScript
Фундаментальный разбор TypeScript: архитектура транспиляции, статическая типизация, интерфейсы, Generic-типы, утилитарные типы, метапрограммирование и оптимизация веб-приложений.
tsc) добавляют новые проверки строгости в tsconfig.json (например, exactOptionalPropertyTypes или noUncheckedIndexedAccess). Рекомендуется всегда использовать флаг "strict": true и сверять специфичные системные типы с официальной документацией TypeScript Handbook.
- 1. Экосистема TypeScript и архитектура транспиляции
- 2. Система типов: Примитивы, Type vs Interface и Literal Types
- 3. Обобщенное программирование (Generics) и ограничения
- 4. Продвинутая типизация: Mapped Types, Conditional Types и Utility Types
- 5. Сужение типов (Type Narrowing), Type Guards и асинхронность
- 6. Конфигурация tsconfig.json, оптимизация и метапрограммирование
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). Это означает, что два типа совместимы, если они имеют одинаковую форму и структуру, независимо от их имени.
- Interface (Интерфейсы): Идеально подходят для описания формы объектов и сервисных контрактов. Поддерживают декларативное слияние (Declaration Merging) и открыты для расширения с помощью
extends. - Type Alias (Псевдонимы типов): Универсальны. Позволяют создавать сложные объединения (Union Types), пересечения (Intersection Types), а также опираться на примитивы и кортежи (Tuples).
- Literal Types и Enums: Позволяют ограничить значения строго определенными строками или числами, обеспечивая точную автодополняемость кодового базиса.
Ниже представлен пример описания структуры пользователя и конфига с синтаксической подсветкой:
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");
}
}