Форум программистов, компьютерный форум, киберфорум
run.dev
Войти
Регистрация
Восстановить пароль

Типы в TypeScript

Запись от run.dev размещена 18.04.2025 в 11:32. Обновил(-а) mik-a-el 28.04.2025 в 13:08
Показов 3070 Комментарии 0
Метки typescript

Нажмите на изображение для увеличения
Название: 26d7a107-d733-4954-9713-41317ca82a85.jpg
Просмотров: 60
Размер:	144.5 Кб
ID:	10609
TypeScript представляет собой мощное расширение JavaScript, которое добавляет статическую типизацию в этот динамический язык. В JavaScript, где переменная может свободно менять тип в процессе выполнения программы, TypeScript вводит строгий порядок и структуру, позволяя разработчикам явно указывать, какие типы данных ожидаются и используются в коде.

Почему типы важны в разработке



Типизация – не просто формальность или дополнительное ограничение. Это фундаментальный инструмент, который кардинально меняет процесс разработки. Прежде всего, статическая типизация значительно снижает вероятность ошибок. Когда вы четко определяете, что функция принимает число и возвращает строку, TypeScript не даст передать ей объект или массив без явного преобразования.

TypeScript
1
2
3
4
5
6
function formatPrice(price: number): string {
    return `$${price.toFixed(2)}`;
}
 
// Ошибка: аргумент типа string не может быть присвоен параметру типа number
formatPrice("100");
Типы также работают как документация, которая никогда не устаревает. Взглянув на сигнатуру функции или интерфейс объекта, разработчик мгновенно понимает, с какими данными ему предстоит работать. Это особенно ценно в больших проектах, где код может читать и модифицировать множество людей. Автодополнение в редакторах кода – еще одно преимущество, которое обеспечивает типизация. Интегрированные среды разработки (IDE) могут предлагать подходящие методы и свойства для объектов, экономя время и снижая количество опечаток.

Почему typescript не проверяет типы при использовании спред-оператора?
Никак не могу разобраться, почему не работает проверка типов в следующем примере: interface...

TypeScript vs Script# vs
У кого какой опыт ? - сравнительные достоинства и недостатки.

Перевод C# на TypeScript
Доброго времени суток))) (Извините если не в ту тему) Существует рабочая программы для локального...

VS2012 + typescript 9.1.1
При работе с TypeScript VS2012 виснет или закрывается регулярно, никакой конкретной информации об...


Отличия от JavaScript



JavaScript – язык с динамической типизацией, где переменная может содержать данные любого типа, и этот тип может меняться в процессе выполнения программы:

JavaScript
1
2
3
let value = 42;        // Сначала value содержит число
value = "Hello";       // Теперь value содержит строку
value = { id: 1 };     // А теперь – объект
TypeScript вводит контроль на этапе компиляции, заставляя разработчиков явно указывать тип переменной или позволяя компилятору вывести его:

TypeScript
1
2
let value: number = 42;
value = "Hello";       // Ошибка: тип string не может быть присвоен типу number
При этом TypeScript является супермножеством JavaScript – любой валидный JavaScript код является также валидным TypeScript кодом. Это означает, что внедрение TypeScript в существующие проекты может происходить постепенно, файл за файлом.

История развития типизации в TS



TypeScript был создан Microsoft в 2012 году, когда Андерс Хейлсберг (также известный как создатель C#) осознал необходимость в инструменте, который бы делал разработку JavaScript более масштабируемой для крупных приложений. С тех пор система типов эволюционировала от базовых примитивов и интерфейсов до сложных конструкций, включающих условные типы, мэппинг типов и дженерики. Каждая новая версия TypeScript расширяла возможности системы типов:

TypeScript 2.0 (2016) ввел null и undefined как отдельные типы и добавил строгую проверку null,
TypeScript 2.1 добавил инференцию для сужения типов (type narrowing),
TypeScript 3.0 (2018) улучшил работу с параметрами rest и spread,
TypeScript 4.0 (2020) представил синтетические типы и улучшения для кортежей.

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



В отличие от таких языков как Java или C#, где типизация обязательна, TypeScript предоставляет возможность постепенного внедрения типов. Система типов TS также более гибкая – она включает структурную типизацию вместо номинальной, используемой в большинстве объектно-ориентированных языков. При структурной типизации два типа считаются совместимыми, если их структура совпадает, независимо от их имен:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
interface Named {
    name: string;
}
 
class Person {
    name: string;
    constructor(name: string) {
        this.name = name;
    }
}
 
// Допустимо, т.к. Person имеет свойство name
const p: Named = new Person("Alice");
В сравнении с Flow (альтернативная система типов для JavaScript от Facebook), TypeScript имеет более активное сообщество, лучшую экосистему инструментов и более глубокую интеграцию с популярными фреймворками. TypeScript также отличается от языков со статической типизацией, таких как Scala или Haskell, меньшей математической строгостью, но большей практичностью и доступностью для среднего разработчика. В итоге, TypeScript занимает уникальную нишу между гибкостью динамически типизированных языков и надежностью статически типизированных, предлагая разумный компромисс для современной веб-разработки.

Базовые типы



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

Примитивные типы: string, number, boolean



В основе системы типов TypeScript лежат три фундаментальных примитива, унаследованных от JavaScript:

String — для работы с текстовыми данными. В TypeScript строки обозначаются ключевым словом string:

TypeScript
1
2
let name: string = "Александр";
let greeting: string = `Привет, ${name}!`;  // Шаблонные строки также поддерживаются
Number — для представления числовых значений. В TypeScript, как и в JavaScript, все числа представлены в формате с плавающей точкой:

TypeScript
1
2
3
4
let decimal: number = 42;
let float: number = 3.14;
let hex: number = 0xf00d;      // шестнадцатеричная запись
let binary: number = 0b1010;   // двоичная запись
Boolean — простейший тип, принимающий только значения true или false:

TypeScript
1
2
let isCompleted: boolean = false;
isCompleted = true;

Особый тип any и когда его избегать



Тип any в TypeScript позволяет переменной принимать значения любых типов, фактически отключая проверку типов:

TypeScript
1
2
3
let dynamicValue: any = 4;
dynamicValue = "строка";      // Допустимо
dynamicValue = { key: "value" }; // Тоже допустимо
Использование any подрывает основное преимущество TypeScript — статическую типизацию. Код с избыточным применением этого типа теряет в надежности и поддерживаемости.

Тип any стоит применять только в исключительных случаях:
  1. При работе с кодом, не имеющим типизации (например, сторонние JavaScript-библиотеки без определений типов).
  2. В процессе постепенной миграции JavaScript-проекта на TypeScript.
  3. Когда тип действительно неизвестен и может меняться непредсказуемо.

В большинстве ситуаций лучше использовать более конкретные типы или конструкции вроде union-типов.

Практические примеры использования



Рассмотрим практические примеры сочетания различных базовых типов:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Функция с типизированными параметрами и возвращаемым значением
function calculateTotal(price: number, quantity: number): number {
    return price * quantity;
}
 
// Объект с указанием типов свойств
let product: { id: number; name: string; inStock: boolean } = {
    id: 1,
    name: "Ноутбук",
    inStock: true
};
 
// Массив чисел
let fibonacci: number[] = [1, 1, 2, 3, 5, 8, 13];
 
// Функциональный тип (сигнатура функции)
let logger: (message: string) => void;
logger = (message) => console.log(`LOG: ${message}`);

Тип null и undefined: особенности и применение



TypeScript различает два специальных типа — null и undefined, отражающие соответствующие значения в JavaScript:

TypeScript
1
2
let empty: null = null;
let notDefined: undefined = undefined;
По умолчанию эти типы являются подтипами всех других типов, что может привести к неожиданным ошибкам. Чтобы сделать их обработку более строгой, в tsconfig.json можно включить параметр strictNullChecks:

JSON
1
2
3
4
5
{
    "compilerOptions": {
        "strictNullChecks": true
    }
}
При строгой проверке null необходимо явно указывать возможность нулевого значения с помощью union-типа:

TypeScript
1
2
3
4
5
// При включенном strictNullChecks это вызовет ошибку
let userName: string = null; 
 
// Правильный способ
let userNameOrNull: string | null = null;

Литеральные типы и их практическая ценность



Литеральные типы позволяют указать, что переменная может принимать только конкретное значение или набор значений:

TypeScript
1
2
3
4
5
6
7
8
9
// Переменная может содержать только конкретную строку
let direction: "north" | "south" | "east" | "west";
direction = "north"; // Допустимо
direction = "northeast"; // Ошибка: Type '"northeast"' is not assignable
 
// Числовые литералы
let diceRoll: 1 | 2 | 3 | 4 | 5 | 6;
diceRoll = 3; // Допустимо
diceRoll = 7; // Ошибка
Литеральные типы особенно полезны для создания типов-перечислений и API с ограниченным набором значений параметров. Они делают код более предсказуемым и улучшают автодополнение.

Работа с массивами и кортежами в TypeScript



TypeScript предлагает два способа объявления массивов:

TypeScript
1
2
3
4
5
// С использованием обобщенного типа Array
let fruits: Array<string> = ["яблоко", "банан", "груша"];
 
// С использованием синтаксиса квадратных скобок
let vegetables: string[] = ["морковь", "капуста", "свекла"];
Оба варианта эквивалентны, но второй более компактен и чаще встречается в кодовых базах.
Кортежи (tuples) — особый вид массивов с фиксированным количеством элементов, где каждая позиция имеет определенный тип:

TypeScript
1
2
3
4
5
// Кортеж, где первый элемент — строка, а второй — число
let nameAndAge: [string, number] = ["Антон", 34];
 
// Деструктуризация кортежа
let [name, age] = nameAndAge;
Кортежи удобны для представления структурированных данных, когда нужно сохранить типы отдельных элементов, но создание полноценного интерфейса избыточно:

TypeScript
1
2
3
4
5
6
7
// Функция, возвращающая кортеж
function getUserInfo(): [string, number, boolean] {
    // Возвращаем имя, возраст и статус активности
    return ["Мария", 28, true];
}
 
const [userName, userAge, isActive] = getUserInfo();
Однако для более сложных структур данных кортежи могут затруднять понимание кода, и в таких случаях лучше использовать интерфейсы или классы с именованными свойствами. Для эффективной работы с TypeScript недостаточно знать только примитивные типы. В арсенале разработчика должны быть и другие базовые типы, которые помогают создавать надёжные и выразительные программы.

Тип unknown: безопасная альтернатива any



Тип unknown был добавлен в TypeScript как более безопасная альтернатива типу any. В отличие от any, unknown требует явной проверки типа перед выполнением операций:

TypeScript
1
2
3
4
5
6
7
8
9
10
let userInput: unknown;
let userName: string;
 
userInput = "Иван";
// userName = userInput; // Ошибка: переменная типа unknown не может быть присвоена переменной типа string
 
// Необходима проверка типа перед использованием
if (typeof userInput === "string") {
    userName = userInput; // Теперь это допустимо
}
Это особенно полезно, когда вы работаете с данными, тип которых заранее неизвестен (например, пользовательский ввод или ответ API), но требуется гарантировать безопасность типов в дальнейшем коде.

Тип void: отсутствие значения



Тип void обычно используется в качестве возвращаемого значения функций, которые ничего не возвращают:

TypeScript
1
2
3
4
5
6
7
function logMessage(message: string): void {
    console.log(message);
    // Без оператора return или с пустым return
}
 
// Переменные типа void могут хранить только undefined или null (при выключенном strictNullChecks)
let unusable: void = undefined;

Тип never: невозможные значения



Тип never представляет значения, которые никогда не могут возникнуть. Он используется в нескольких сценариях:

1. Функции, которые никогда не завершаются (бросают исключения или содержат бесконечные циклы):

TypeScript
1
2
3
4
5
6
7
8
9
function throwError(message: string): never {
    throw new Error(message);
}
 
function infiniteLoop(): never {
    while (true) {
        // Что-то делаем бесконечно
    }
}
2. В сужении типов, когда все возможные варианты исчерпаны:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function assertNever(value: never): never {
    throw new Error(`Недопустимое значение: ${value}`);
}
 
type Shape = Circle | Square;
 
function getArea(shape: Shape) {
    if ('radius' in shape) {
        return Math.PI * shape.radius [B] 2;
    } else if ('sideLength' in shape) {
        return shape.sideLength [/B] 2;
    } else {
        // Если в будущем тип Shape расширится новыми вариантами,
        // TypeScript укажет на ошибку в этой строке
        return assertNever(shape);
    }
}

Перечисления (enum)



Перечисления позволяют определить набор именованных констант, что делает код более читаемым и устойчивым к ошибкам:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
enum Direction {
    North,
    East,
    South,
    West
}
 
let currentDirection: Direction = Direction.North;
 
// По умолчанию значения начинаются с 0
console.log(Direction.North); // 0
console.log(Direction.East);  // 1
 
// Можно задать начальное значение
enum HttpStatus {
    OK = 200,
    NotFound = 404,
    InternalServerError = 500
}
 
function handleResponse(status: HttpStatus) {
    if (status === HttpStatus.OK) {
        console.log("Запрос выполнен успешно");
    }
}
Строковые перечисления позволяют использовать строки вместо чисел, что часто более наглядно:

TypeScript
1
2
3
4
5
6
7
8
enum Color {
    Red = "RED",
    Green = "GREEN",
    Blue = "BLUE"
}
 
let favoriteColor: Color = Color.Blue;
console.log(favoriteColor); // "BLUE"

Объектные типы и интерфейсы



Для типизации объектов TypeScript предлагает два основных подхода: анонимные объектные типы и интерфейсы.
Анонимные объектные типы полезны для одноразового использования:

TypeScript
1
2
3
4
5
6
7
8
9
10
let user: { id: number; name: string; email?: string } = {
    id: 1,
    name: "Анна"
    // email не обязателен благодаря модификатору "?"
};
 
// Функция, принимающая объект со свойствами x и y
function printPoint(point: { x: number; y: number }): void {
    console.log(`X: ${point.x}, Y: ${point.y}`);
}
Интерфейсы предпочтительны для повторного использования типов и более сложных структур:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface User {
    id: number;
    name: string;
    email?: string;
    readonly createdAt: Date;
    updateProfile(newName: string): void;
}
 
let admin: User = {
    id: 1,
    name: "Администратор",
    createdAt: new Date(),
    updateProfile(newName) {
        this.name = newName;
    }
};
 
// Нельзя изменить свойство, помеченное как readonly
// admin.createdAt = new Date(); // Ошибка

Тип object и Record



Тип object представляет любое нестроковое, нечисловое и небулевое значение:

TypeScript
1
2
3
4
5
let obj: object = { key: "value" };
// obj = 42; // Ошибка: Type 'number' is not assignable to type 'object'
 
// Однако доступ к свойствам ограничен
// console.log(obj.key); // Ошибка: Property 'key' does not exist on type 'object'
Из-за этих ограничений обычно предпочтительнее использовать конкретные объектные типы или универсальные структуры вроде Record:

TypeScript
1
2
3
4
5
6
7
8
9
// Record<K, V> создаёт объект с ключами типа K и значениями типа V
let dictionary: Record<string, number> = {
    "one": 1,
    "two": 2,
    "three": 3
};
 
// Можно добавлять новые пары ключ-значение
dictionary["four"] = 4;

Типы bigint и symbol



TypeScript поддерживает новые примитивные типы JavaScript: bigint и symbol.

bigint используется для работы с целыми числами произвольной точности:

TypeScript
1
2
3
// Для использования bigint необходимо указать target ES2020 или выше в tsconfig.json
const bigNumber: bigint = 1234567890123456789012345678901234567890n;
const anotherBig: bigint = BigInt("9007199254740991");
symbol создаёт уникальные идентификаторы, часто используемые как ключи свойств:

TypeScript
1
2
3
4
5
6
7
8
9
10
const key1: symbol = Symbol("description");
const key2: symbol = Symbol("description");
 
console.log(key1 === key2); // false, символы всегда уникальны
 
const obj = {
    [key1]: "Значение, доступное через символьный ключ"
};
 
console.log(obj[key1]); // "Значение, доступное через символьный ключ"

Утверждения типов (Type Assertions)



Иногда разработчик лучше компилятора знает, какой тип имеет значение. В таких случаях можно использовать утверждения типов:

TypeScript
1
2
3
4
5
6
7
8
// Предположим, что getElementById возвращает HTMLElement или null
const input = document.getElementById("username") as HTMLInputElement;
 
// Теперь TypeScript "знает", что input — это HTMLInputElement
console.log(input.value);
 
// Альтернативный синтаксис (менее распространен)
const input2 = <HTMLInputElement>document.getElementById("password");
Утверждения не изменяют тип во время выполнения и не проводят преобразования — они только информируют компилятор о типе.

Немного об операторе typeof



TypeScript расширяет возможности оператора typeof для создания типов на основе значений:

TypeScript
1
2
3
4
5
6
7
const point = { x: 10, y: 20 };
type Point = typeof point; // { x: number; y: number; }
 
function greet(name: string, age: number) {
    return `Привет, ${name}! Тебе ${age} лет.`;
}
type GreetFunction = typeof greet; // (name: string, age: number) => string
Это особенно полезно при работе с существующими библиотеками или миграции кода с JavaScript на TypeScript.
понимание базовых типов TypeScript закладывает прочную основу для создания надёжного и поддерживаемого кода. Эти инструменты позволяют не только обнаруживать ошибки на этапе компиляции, но и делают код более читаемым и документированным, что критически важно для долгосрочных проектов и командной разработки.

Продвинутые типы



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

Union и Intersection типы



Union-типы (объединения) позволяют указать, что значение может иметь один из нескольких типов. Они обозначаются с помощью вертикальной черты |:

TypeScript
1
2
3
4
5
// Переменная может содержать либо строку, либо число
let id: string | number;
id = "abc123"; // Корректно
id = 42;       // Тоже корректно
id = true;     // Ошибка: тип boolean не входит в union string | number
Union-типы особенно полезны для функций, которые могут работать с разными типами данных:

TypeScript
1
2
3
4
5
6
7
function formatId(id: string | number): string {
  if (typeof id === "string") {
    return id.toUpperCase();
  } else {
    return `ID-${id.toString().padStart(6, '0')}`;
  }
}
Intersection-типы (пересечения) объединяют несколько типов в один, содержащий все свойства исходных типов. Они создаются с помощью амперсанда &:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
type Employee = {
  id: number;
  name: string;
};
 
type Manager = {
  employees: Employee[];
  departmentId: number;
};
 
// Тип, объединяющий свойства Employee и Manager
type ManagerWithDetails = Employee & Manager;
 
const director: ManagerWithDetails = {
  id: 1,
  name: "Анна Иванова",
  employees: [{id: 2, name: "Иван Петров"}],
  departmentId: 100
};

Дженерики и их применение



Дженерики (обобщённые типы) — это способ создания компонентов, которые могут работать с различными типами, сохраняя при этом типобезопасность:

TypeScript
1
2
3
4
5
6
7
8
9
// Функция identity возвращает то же значение, что получила
function identity<T>(arg: T): T {
  return arg;
}
 
// Явное указание типа
const numResult = identity<number>(42);
// Вывод типа
const strResult = identity("hello"); // TypeScript выведет T как string
Дженерики часто используются при создании повторно используемых компонентов и контейнеров:

TypeScript
1
2
3
4
5
6
7
8
// Обобщённый интерфейс для контейнера
interface Box<T> {
  value: T;
}
 
// Использование с конкретным типом
const numberBox: Box<number> = { value: 42 };
const stringBox: Box<string> = { value: "hello" };
Дженерики позволяют задавать ограничения на типы параметров:

TypeScript
1
2
3
4
5
6
7
8
9
// T должен иметь свойство length
function getLength<T extends { length: number }>(arg: T): number {
  return arg.length;
}
 
getLength("string");     // Работает: у строк есть свойство length
getLength([1, 2, 3]);    // Работает: у массивов есть свойство length
getLength({ length: 5 }); // Работает: у объекта есть свойство length
// getLength(42);       // Ошибка: у чисел нет свойства length

Типы-утилиты



TypeScript предоставляет набор встроенных типов-утилит для преобразования существующих типов:

Partial<T> — делает все свойства типа T необязательными:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
interface User {
  id: number;
  name: string;
  email: string;
}
 
// Все свойства становятся необязательными
function updateUser(userId: number, updates: Partial<User>) {
  // Можно передать только нужные поля
}
 
updateUser(1, { name: "Новое имя" }); // Не требуется email
Required<T> — противоположность Partial, делает все свойства обязательными:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
interface Config {
  host?: string;
  port?: number;
  secure?: boolean;
}
 
// Все свойства становятся обязательными
const fullConfig: Required<Config> = {
  host: "localhost",
  port: 8080,
  secure: true
};
Readonly<T> — делает все свойства доступными только для чтения:

TypeScript
1
2
3
4
5
6
7
const frozenUser: Readonly<User> = {
  id: 1,
  name: "Иван",
  email: "ivan@example.com"
};
 
// frozenUser.name = "Петр"; // Ошибка: нельзя изменить readonly свойство
Pick<T, K> — создаёт тип из подмножества свойств T:

TypeScript
1
2
3
4
5
6
7
8
// Только id и name из интерфейса User
type BasicUser = Pick<User, "id" | "name">;
 
const basicUser: BasicUser = {
  id: 1,
  name: "Иван"
  // email не нужен
};
Omit<T, K> — создаёт тип, исключая указанные свойства:

TypeScript
1
2
3
4
5
6
7
8
// Все свойства User кроме email
type UserWithoutEmail = Omit<User, "email">;
 
const userNoEmail: UserWithoutEmail = {
  id: 1,
  name: "Иван"
  // email не разрешен
};
Record<K, T> — создаёт тип с ключами типа K и значениями типа T:

TypeScript
1
2
3
4
5
6
// Объект с числовыми ключами и строковыми значениями
const ages: Record<number, string> = {
  10: "десять",
  20: "двадцать",
  30: "тридцать"
};

Conditional Types и mapped types для создания гибких систем



Условные типы (Conditional Types) позволяют выбрать тип на основе условия:

TypeScript
1
2
3
4
5
6
// T extends U ? X : Y — если T является подтипом U, выбираем X, иначе Y
type IsString<T> = T extends string ? true : false;
 
// Примеры использования
type A = IsString<"hello">; // true
type B = IsString<42>;      // false
Условные типы часто используются вместе с дженериками для создания гибких и адаптивных систем типов:

TypeScript
1
2
3
4
5
6
7
8
// Извлекаем тип возвращаемого значения функции
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
 
function fetchUser() {
  return { id: 1, name: "Анна" };
}
 
type User = ReturnType<typeof fetchUser>; // { id: number; name: string; }
Mapped Types (преобразованные типы) позволяют трансформировать каждое свойство исходного типа:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
// Делаем все свойства объекта необязательными
type Optional<T> = {
  [P in keyof T]?: T[P];
};
 
interface Person {
  name: string;
  age: number;
}
 
// Все свойства стали необязательными
const partialPerson: Optional<Person> = { name: "Иван" };
Можно комбинировать mapped types с другими конструкциями:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
// Делаем все свойства доступными только для чтения и строковыми
type ReadonlyStringified<T> = {
  readonly [P in keyof T]: string;
};
 
const stringPerson: ReadonlyStringified<Person> = {
  name: "Иван",
  age: "30" // Теперь это строка
};
 
// stringPerson.name = "Петр"; // Ошибка: нельзя изменить readonly свойство
Продвинутые типы в TypeScript предоставляют богатый инструментарий для моделирования сложной бизнес-логики и создания гибких, типобезопасных API. Они помогают предотвращать потенциальные ошибки и сделать код более самодокументированным, что особенно ценно в крупных проектах с множеством разработчиков. TypeScript продолжает эволюционировать, и новые версии регулярно добавляют мощные возможности к системе типов, позволяя ещё точнее моделировать отношения между данными и упрощая разработку сложных приложений.

Type Guards и сужение типов (Type Narrowing)



Type Guards (защитники типов) — это конструкции, позволяющие TypeScript сужать тип переменной в определённом блоке кода. Они особенно полезны при работе с union-типами, когда нужно выполнить определённые операции в зависимости от конкретного типа значения. Базовые защитники типов используют оператор typeof:

TypeScript
1
2
3
4
5
6
7
8
9
function processValue(value: string | number) {
  // В этом блоке TypeScript знает, что value — строка
  if (typeof value === "string") {
    return value.toUpperCase();
  }
  
  // А в этом блоке — число
  return value.toFixed(2);
}
Для проверки свойств объектов можно использовать оператор in:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface Bird {
  fly(): void;
  layEggs(): void;
}
 
interface Fish {
  swim(): void;
  layEggs(): void;
}
 
function move(animal: Bird | Fish) {
  if ("fly" in animal) {
    // TypeScript знает, что здесь animal — это Bird
    animal.fly();
  } else {
    // А здесь — Fish
    animal.swim();
  }
}
Для классов работает оператор instanceof:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Car {
  drive() { console.log("Вррум!"); }
}
 
class Bicycle {
  ride() { console.log("Крутим педали!"); }
}
 
function useVehicle(vehicle: Car | Bicycle) {
  if (vehicle instanceof Car) {
    vehicle.drive();
  } else {
    vehicle.ride();
  }
}

Пользовательские защитники типов



Иногда встроенных защитников недостаточно. В таких случаях можно создать функцию-предикат, возвращающую булево значение и имеющую специальную сигнатуру parameterName is Type:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
interface Rectangle {
  kind: "rectangle";
  width: number;
  height: number;
}
 
interface Circle {
  kind: "circle";
  radius: number;
}
 
type Shape = Rectangle | Circle;
 
// Пользовательский защитник типа
function isRectangle(shape: Shape): shape is Rectangle {
  return shape.kind === "rectangle";
}
 
function calculateArea(shape: Shape): number {
  if (isRectangle(shape)) {
    // TypeScript знает, что shape — это Rectangle
    return shape.width * shape.height;
  } else {
    // А здесь shape — это Circle
    return Math.PI * shape.radius ** 2;
  }
}

Дискриминантные объединения (Discriminated Unions)



Часто для union-типов используется общее свойство-дискриминатор (например, kind или type), которое позволяет однозначно определить конкретный тип:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
interface Square {
  kind: "square";
  size: number;
}
 
interface Rectangle {
  kind: "rectangle";
  width: number;
  height: number;
}
 
interface Circle {
  kind: "circle";
  radius: number;
}
 
type Shape = Square | Rectangle | Circle;
 
function calculateArea(shape: Shape): number {
  switch (shape.kind) {
    case "square":
      return shape.size ** 2;
    case "rectangle":
      return shape.width * shape.height;
    case "circle":
      return Math.PI * shape.radius ** 2;
    default:
      // TypeScript проверит, что все варианты обработаны
      const _exhaustiveCheck: never = shape;
      return _exhaustiveCheck;
  }
}
Обратите внимание на последнюю строку с _exhaustiveCheck. Это техника, которая гарантирует, что все возможные типы обработаны: если в будущем тип Shape будет расширен, но обработчик не обновлен, компилятор выдаст ошибку.

Типизация классов и интерфейсов: различия и особенности



В TypeScript интерфейсы и классы тесно связаны, но имеют различные цели и особенности применения.

Интерфейсы для описания структуры



Интерфейсы определяют структуру объекта, не предоставляя реализации:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Drawable {
  draw(): void;
  resize(width: number, height: number): void;
}
 
// Класс, реализующий интерфейс
class Circle implements Drawable {
  constructor(private radius: number) {}
  
  draw() {
    console.log(`Рисуем круг радиусом ${this.radius}`);
  }
  
  resize(width: number, height: number) {
    this.radius = Math.min(width, height) / 2;
  }
}
Интерфейсы могут расширять друг друга, создавая более сложные структуры:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Person {
  name: string;
  age: number;
}
 
interface Employee extends Person {
  employeeId: string;
  department: string;
}
 
// Объект должен содержать все поля из обоих интерфейсов
const manager: Employee = {
  name: "Иван",
  age: 35,
  employeeId: "EMP123",
  department: "IT"
};

Классы: объединение данных и поведения



Классы объединяют структуру данных и логику поведения:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class Animal {
  // Модификаторы доступа: public, private, protected
  constructor(protected name: string) {}
  
  move(distance: number = 0): void {
    console.log(`${this.name} передвигается на ${distance}м.`);
  }
}
 
class Dog extends Animal {
  constructor(name: string, private breed: string) {
    super(name);
  }
  
  bark(): void {
    console.log("Гав-гав!");
  }
  
  // Переопределение метода родительского класса
  move(distance: number = 5): void {
    console.log(`${this.name} породы ${this.breed} бежит...`);
    super.move(distance);
  }
}
 
const dog = new Dog("Рекс", "Овчарка");
dog.bark();
dog.move(10);

Абстрактные классы



Абстрактные классы занимают промежуточное положение между интерфейсами и обычными классами. Они могут содержать как абстрактные методы (без реализации), так и конкретные:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
abstract class Shape {
  constructor(protected color: string) {}
  
  // Абстрактный метод, должен быть реализован в подклассах
  abstract calculateArea(): number;
  
  // Конкретный метод с реализацией
  display(): void {
    console.log(`Фигура цвета ${this.color}`);
  }
}
 
class Rectangle extends Shape {
  constructor(
    color: string, 
    private width: number, 
    private height: number
  ) {
    super(color);
  }
  
  calculateArea(): number {
    return this.width * this.height;
  }
  
  // Дополнительный метод, специфичный для прямоугольника
  get diagonal(): number {
    return Math.sqrt(this.width [B] 2 + this.height [/B] 2);
  }
}

Основные различия между классами и интерфейсами



1. Реализация: интерфейсы не содержат реализации, классы могут содержать как объявления, так и реализацию.
2. Экземпляры: нельзя создать экземпляр интерфейса, можно создать экземпляр класса.
3. Расширение: класс может реализовать множество интерфейсов, но расширить только один класс.
4. Видимость: классы поддерживают модификаторы доступа (public, private, protected), интерфейсы — нет.
5. Слияние деклараций: несколько интерфейсов с одинаковым именем автоматически объединяются, с классами такого не происходит.

Приватные и защищенные поля



TypeScript расширяет JavaScript, добавляя концепции приватных и защищенных полей:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class BankAccount {
  // Публичное свойство, доступно везде
  public owner: string;
  
  // Приватное свойство, доступно только внутри класса
  private balance: number;
  
  // Защищенное свойство, доступно в классе и подклассах
  protected accountNumber: string;
  
  constructor(owner: string, initialBalance: number) {
    this.owner = owner;
    this.balance = initialBalance;
    this.accountNumber = `ACC${Math.floor(Math.random() * 10000)}`;
  }
  
  deposit(amount: number): void {
    this.balance += amount;
  }
  
  getBalance(): number {
    return this.balance;
  }
}
 
class SavingsAccount extends BankAccount {
  private interestRate: number;
  
  constructor(owner: string, initialBalance: number, interestRate: number) {
    super(owner, initialBalance);
    this.interestRate = interestRate;
  }
  
  addInterest(): void {
    // Можем использовать accountNumber (protected)
    console.log(`Начисляем проценты на счет ${this.accountNumber}`);
    // Но не можем напрямую обратиться к balance (private)
    // Используем публичный метод
    const currentBalance = this.getBalance();
    this.deposit(currentBalance * this.interestRate);
  }
}
В TypeScript 3.8+ также поддерживаются приватные поля ECMAScript, которые обеспечивают истинную приватность на уровне языка JavaScript:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person {
  #secretId: string;
  
  constructor(public name: string) {
    this.#secretId = `secret-${Math.random()}`;
  }
  
  getSecretId(): string {
    return this.#secretId;
  }
}
 
const person = new Person("Анна");
console.log(person.name);       // OK
// console.log(person.#secretId);  // Ошибка: приватное поле

Namespace и модули: организация типов в больших проектах



Для организации кода в крупных проектах TypeScript предлагает два механизма: пространства имен (namespaces) и модули (modules).

Пространства имен



Пространства имен группируют связанные функциональности и помогают избежать конфликтов имен:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
namespace Geometry {
  export interface Point {
    x: number;
    y: number;
  }
  
  export class Line {
    constructor(public start: Point, public end: Point) {}
    
    length(): number {
      const dx = this.end.x - this.start.x;
      const dy = this.end.y - this.start.y;
      return Math.sqrt(dx * dx + dy * dy);
    }
  }
  
  // Вложенное пространство имен
  export namespace ThreeDimensional {
    export interface Point3D extends Point {
      z: number;
    }
  }
}
 
// Использование
const point1: Geometry.Point = { x: 0, y: 0 };
const point2: Geometry.Point = { x: 3, y: 4 };
const line = new Geometry.Line(point1, point2);
console.log(line.length()); // 5
 
// Использование вложенного пространства имен
const point3D: Geometry.ThreeDimensional.Point3D = { x: 1, y: 2, z: 3 };

Модули в TypeScript



В отличие от пространств имен, модули существуют на уровне файлов и предназначены для работы с современными системами модулей JavaScript (ES Modules, CommonJS):

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
// math.ts
export function add(a: number, b: number): number {
  return a + b;
}
 
export function subtract(a: number, b: number): number {
  return a - b;
}
 
export interface MathOperation {
  (a: number, b: number): number;
}
TypeScript
1
2
3
4
5
6
7
8
9
10
// app.ts
import { add, MathOperation } from './math';
// Импорт всего модуля
import * as Math from './math';
 
const sum = add(5, 3);
const difference = Math.subtract(10, 4);
 
// Использование импортированного интерфейса
const multiply: MathOperation = (a, b) => a * b;
В современной разработке модули считаются предпочтительным способом организации кода, в то время как пространства имен используются в основном в старых кодовых базах или для внутренних структур.

Индексные типы и операторы доступа к ключам



TypeScript предоставляет операторы для работы с ключами типов и их значениями.

Оператор keyof



Оператор keyof создает объединение строковых литералов из ключей объектного типа:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Person {
  name: string;
  age: number;
  address: string;
}
 
// 'name' | 'age' | 'address'
type PersonKeys = keyof Person;
 
// Функция для безопасного получения свойства объекта по ключу
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}
 
const john: Person = {
  name: "Иван",
  age: 30,
  address: "ул. Примерная, 123"
};
 
const johnName = getProperty(john, "name");  // строка
const johnAge = getProperty(john, "age");    // число
// const error = getProperty(john, "job");   // Ошибка: "job" не является ключом Person

Индексные типы доступа



Оператор T[K] позволяет получить тип значения по ключу:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
interface Dictionary<T> {
  [key: string]: T;
}
 
const stringDict: Dictionary<string> = {
  "a": "apple",
  "b": "banana"
};
 
// Получаем тип элементов словаря: string
type DictValue = Dictionary<string>["a"];
 
// С ограниченным набором ключей
interface PersonWithCategories {
  name: string;
  age: number;
  categories: {
    primary: string;
    secondary: string;
  };
}
 
// Тип для категорий: { primary: string; secondary: string; }
type Categories = PersonWithCategories["categories"];
 
// Тип для первичной категории: string
type PrimaryCategory = PersonWithCategories["categories"]["primary"];

Строковые литералы и шаблонные литеральные типы



TypeScript 4.1 и выше поддерживает шаблонные литеральные типы, которые работают аналогично шаблонным строкам, но на уровне типов:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
type Color = "red" | "green" | "blue";
type Quantity = "one" | "two" | "three";
 
// "one_red" | "one_green" | "one_blue" | "two_red" | ... и т.д.
type ColorQuantity = `${Quantity}_${Color}`;
 
// Преобразование ключей в camelCase
type CamelCase<S extends string> = S extends `${infer P}_${infer Q}`
  ? `${P}${Capitalize<Q>}`
  : S;
 
// Пример использования: "user_id" -> "userId"
type CamelCaseTest = CamelCase<"user_id">;
 
// Преобразование всех ключей объекта
type CamelCaseKeys<T> = {
  [K in keyof T as CamelCase<string & K>]: T[K]
};
 
interface ApiResponse {
  user_id: number;
  first_name: string;
  last_name: string;
}
 
// Результат: { userId: number; firstName: string; lastName: string; }
type NiceResponse = CamelCaseKeys<ApiResponse>;

Рекурсивные типы



Типы в TypeScript могут быть рекурсивными, что полезно для представления древовидных структур или вложенных данных:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
// Рекурсивный тип для древовидной структуры
type TreeNode<T> = {
  value: T;
  children?: TreeNode<T>[];
};
 
// Использование
const tree: TreeNode<string> = {
  value: "root",
  children: [
    { value: "child1" },
    { 
      value: "child2",
      children: [
        { value: "grandchild1" },
        { value: "grandchild2" }
      ]
    }
  ]
};
 
// Рекурсивный тип для JSON-подобных данных
type JSONValue =
  | string
  | number
  | boolean
  | null
  | JSONValue[]
  | { [key: string]: JSONValue };
 
const data: JSONValue = {
  name: "Документ",
  created: true,
  value: 42,
  metadata: {
    author: "Иван",
    tags: ["важное", "документация"]
  }
};

Декораторы и метаданные



Декораторы — экспериментальная возможность TypeScript, которая позволяет добавлять аннотации и метапрограммирование к классам и их членам. Они популярны в фреймворках вроде Angular и NestJS:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// Включите "experimentalDecorators": true в tsconfig.json
 
// Декоратор класса
function Logger(target: Function) {
  console.log(`Создан класс: ${target.name}`);
}
 
// Декоратор свойства
function Property(target: any, propertyKey: string) {
  console.log(`Определено свойство: ${propertyKey}`);
}
 
// Декоратор метода
function Method(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const original = descriptor.value;
  descriptor.value = function(...args: any[]) {
    console.log(`Вызов ${propertyKey} с аргументами: ${JSON.stringify(args)}`);
    return original.apply(this, args);
  };
}
 
@Logger
class Example {
  @Property
  name: string;
 
  constructor(name: string) {
    this.name = name;
  }
 
  @Method
  greet(message: string): string {
    return `${this.name} говорит: ${message}`;
  }
}
 
const example = new Example("Анна");
console.log(example.greet("Привет!"));
// Вывод:
// Создан класс: Example
// Определено свойство: name
// Вызов greet с аргументами: ["Привет!"]
// Анна говорит: Привет!

Типы расширения миксинов (Mixin Types)



Миксины позволяют создавать классы, которые комбинируют функциональность из нескольких источников:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// Базовый класс
class Base {
  isBase = true;
  baseMethod() { return "base method"; }
}
 
// Миксин, добавляющий функциональность таймштампов
type Constructor<T = {}> = new (...args: any[]) => T;
 
function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = Date.now();
    
    getTimestamp() {
      return new Date(this.timestamp);
    }
  };
}
 
// Миксин для идентификации
function Identified<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    id = Math.random().toString(36).substring(2, 9);
    
    getId() {
      return this.id;
    }
  };
}
 
// Объединение миксинов
const TimestampedBase = Timestamped(Base);
const IdentifiedTimestampedBase = Identified(TimestampedBase);
 
// Использование комбинированного класса
const instance = new IdentifiedTimestampedBase();
console.log(instance.isBase);       // true
console.log(instance.baseMethod()); // "base method"
console.log(instance.getTimestamp()); // текущая дата
console.log(instance.getId());      // случайный ID
Продвинутые типы TypeScript позволяют моделировать сложные зависимости и отношения между данными, которые невозможно выразить в обычном JavaScript. Они повышают безопасность и читаемость кода, что особенно важно для крупных проектов и коллективной разработки.

Типизация в реальных проектах



Применение TypeScript в реальных проектах существенно отличается от академического изучения типов. Здесь мы сталкиваемся с ограничениями существующего кода, техническим долгом, производительностью и необходимостью интеграции с нетипизированными библиотеками. Давайте рассмотрим, как эффективно использовать типизацию в боевых условиях.

Частые ошибки и их решения



При работе с TypeScript разработчики часто сталкиваются с типичными проблемами, которые имеют проверенные решения.

Злоупотребление типом any



Одна из самых распространённых ошибок — чрезмерное использование типа any. Этот тип отключает проверку и может создать ложное чувство безопасности:

TypeScript
1
2
3
4
// Анти-паттерн
function processData(data: any) {
  return data.someProperty.method(); // Может взорваться в рантайме
}
Решение: Используйте unknown как более безопасную альтернативу, требующую явной проверки типа:

TypeScript
1
2
3
4
5
6
7
function processData(data: unknown) {
  if (typeof data === 'object' && data !== null && 'someProperty' in data) {
    const item = data as { someProperty: { method: () => void } };
    return item.someProperty.method();
  }
  throw new Error('Некорректный формат данных');
}

Неправильное использование утверждений типа



Частое применение оператора as для утверждения типов может нивелировать преимущества статической типизации:

TypeScript
1
2
// Анти-паттерн
const userData = JSON.parse(response) as UserData; // Опасно!
Решение: Валидируйте данные перед преобразованием типа:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function isUserData(data: unknown): data is UserData {
  return (
    typeof data === 'object' &&
    data !== null &&
    'id' in data &&
    'name' in data &&
    typeof (data as any).id === 'number' &&
    typeof (data as any).name === 'string'
  );
}
 
const responseData = JSON.parse(response);
if (isUserData(responseData)) {
  const userData: UserData = responseData;
  // Безопасно использовать userData
} else {
  throw new Error('Некорректный формат пользовательских данных');
}

Забывание про обработку null и undefined



Многие ошибки возникают из-за неучтённых краевых случаев с null и undefined:

TypeScript
1
2
3
4
// Анти-паттерн
function getFirstItem(items: string[]) {
  return items[0].toUpperCase(); // Может вызвать ошибку, если массив пуст
}
Решение: Используйте строгую проверку null и защитные проверки:

TypeScript
1
2
3
4
5
6
7
8
9
function getFirstItem(items: string[]): string | undefined {
  const item = items[0];
  return item ? item.toUpperCase() : undefined;
}
 
// Или с оператором опциональной последовательности
function getFirstItem(items: string[]): string | undefined {
  return items[0]?.toUpperCase();
}

Оптимизация работы с типами



Правильная организация типов приводит к более производительному и поддерживаемому коду.

Используйте файлы определений типов



Размещайте общие типы в отдельных файлах для улучшения модульности и переиспользования:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// types.ts
export interface User {
  id: number;
  name: string;
  email: string;
}
 
export type UserRole = 'admin' | 'moderator' | 'user';
 
export interface AuthResponse {
  user: User;
  token: string;
  role: UserRole;
}

Избегайте дублирования типов



Дублирование типов создаёт проблемы при изменении структуры данных. Вместо этого используйте композицию и наследование типов:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Анти-паттерн
interface User {
  id: number;
  name: string;
  email: string;
}
 
interface UserWithRole { // Дублирование полей
  id: number;
  name: string;
  email: string;
  role: string;
}
 
// Правильный подход
interface User {
  id: number;
  name: string;
  email: string;
}
 
interface UserWithRole extends User {
  role: string;
}

Используйте утилитные типы для упрощения сложных структур



TypeScript предоставляет мощные утилитные типы, которые избавляют от необходимости создавать собственные преобразования:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
  timestamp: string;
}
 
// Получение только полезных данных
type ResponseData<T> = ApiResponse<T>['data'];
 
// Частичные обновления
interface User {
  id: number;
  name: string;
  email: string;
  settings: {
    theme: string;
    notifications: boolean;
  };
}
 
// Тип для обновления только настроек
type UserSettingsUpdate = Partial<Pick<User, 'settings'>>;
 
// Использование
function updateUserSettings(userId: number, settings: UserSettingsUpdate) {
  // Реализация обновления
}

Инструменты и советы для эффективной типизации



Экосистема TypeScript включает множество инструментов, упрощающих разработку.

Настройка tsconfig.json



Корректная настройка компилятора критически важна для эффективной работы:

JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
  "compilerOptions": {
    "target": "es2018",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "noImplicitThis": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}
Включение строгих проверок (strict: true) поможет выявить потенциальные проблемы на ранних этапах.

Линтеры и форматирование



ESLint с плагином TypeScript обеспечивает единообразие кода и выявляет типичные проблемы:

JSON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// .eslintrc.json
{
  "parser": "@typescript-eslint/parser",
  "plugins": ["@typescript-eslint"],
  "extends": [
    "eslint:recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  "rules": {
    "@typescript-eslint/explicit-function-return-type": "warn",
    "@typescript-eslint/no-explicit-any": "error",
    "@typescript-eslint/no-unused-vars": "error"
  }
}

Автоматическая генерация типов



Для API и GraphQL можно использовать инструменты автоматической генерации типов из схемы:

Bash
1
2
3
4
5
# Для REST API из JSON или OpenAPI
npx openapi-typescript https://api.example.com/swagger.json -o src/types/api.ts
 
# Для GraphQL
npx graphql-codegen --config codegen.yml

Стратегии миграции JavaScript-проектов на TypeScript



Миграция существующего JavaScript-проекта на TypeScript — процесс, требующий планирования.

Постепенный подход



Наиболее безопасная стратегия — постепенная миграция:

1. Настройте проект для сосуществования JS и TS:

JSON
1
2
3
4
5
6
7
8
9
10
// tsconfig.json
{
  "compilerOptions": {
    "allowJs": true,
    "checkJs": false,
    "outDir": "dist",
    "strict": false  // Начните с менее строгих настроек
  },
  "include": ["src/**/*.ts", "src/**/*.js"]
}
2. Переименуйте файлы .js в .ts без изменения кода.

3. Добавляйте типы постепенно, начиная с any где необходимо:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// До миграции (js)
function calculateTotal(items) {
  return items.reduce((total, item) => total + item.price * item.quantity, 0);
}
 
// Начальная миграция (ts)
function calculateTotal(items: any[]): number {
  return items.reduce((total, item) => total + item.price * item.quantity, 0);
}
 
// Полная типизация
interface CartItem {
  id: string;
  price: number;
  quantity: number;
}
 
function calculateTotal(items: CartItem[]): number {
  return items.reduce((total, item) => total + item.price * item.quantity, 0);
}
4. Включайте строгие проверки постепенно:

JSON
1
2
3
4
5
6
{
  "compilerOptions": {
    "noImplicitAny": true,
    // Другие строгие опции добавляйте по мере готовности
  }
}

Использование JSDoc для переходного периода



JSDoc-комментарии позволяют добавлять типизацию в JavaScript перед полной миграцией:

JavaScript
1
2
3
4
5
6
7
8
/**
 * Рассчитывает общую стоимость заказа.
 * @param {Array<{id: string, price: number, quantity: number}>} items Элементы заказа
 * @returns {number} Общая стоимость
 */
function calculateTotal(items) {
  return items.reduce((total, item) => total + item.price * item.quantity, 0);
}
TypeScript распознает JSDoc-аннотации, что позволяет получить преимущества типизации без изменения файлов.

Производительность и оптимизация компилятора TypeScript



При работе с крупными проектами производительность компилятора TypeScript может стать узким местом в процессе разработки. Замедление сборки и задержки при проверке типов снижают эффективность разработчиков.

Оптимизация настроек компилятора



Некоторые настройки в tsconfig.json напрямую влияют на скорость компиляции:

JSON
1
2
3
4
5
6
7
8
{
  "compilerOptions": {
    "incremental": true,
    "skipLibCheck": true,
    "sourceMap": false,
    "isolatedModules": true
  }
}
incremental: true включает инкрементальную компиляцию, сохраняя информацию о предыдущей компиляции.
skipLibCheck: true пропускает проверку типов в файлах определений (.d.ts).
sourceMap: false отключает генерацию source maps в процессе разработки, если они не нужны.
isolatedModules: true заставляет писать код в модульном стиле что ускоряет параллельную компиляцию.

Разделение кода на модули



Разбиение кода на небольшие модули повышает производительность благодаря возможностям параллельной компиляции:

TypeScript
1
2
3
4
5
6
7
8
// Вместо одного большого файла
// components.ts
 
// Разделите на несколько маленьких
// components/Button.ts
// components/Input.ts
// components/Card.ts
// components/index.ts (реэкспорт)

Избегайте сложных типов



Сложные условные и распределённые типы могут значительно замедлить проверку типов:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// Медленный тип с глубокой вложенностью
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object
    ? T[P] extends Function
      ? T[P]
      : DeepReadonly<T[P]>
    : T[P]
};
 
// Более эффективная альтернатива для больших объектов
interface ReadonlyUser {
  readonly id: number;
  readonly name: string;
  readonly settings: ReadonlySettings;
}

Типизация асинхронного кода и Promise



Асинхронное программирование — основа современной веб-разработки, и TypeScript предоставляет инструменты для типизации асинхронного кода.

Базовая типизация Promise



TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Возвращает Promise с конкретным типом результата
async function fetchUserData(id: number): Promise<UserData> {
  const response = await fetch(`/api/users/${id}`);
  if (!response.ok) {
    throw new Error(`HTTP error: ${response.status}`);
  }
  const data = await response.json();
  return data as UserData;
}
 
// Использование
async function displayUser(id: number): Promise<void> {
  try {
    const user = await fetchUserData(id);
    console.log(`Загружен пользователь: ${user.name}`);
  } catch (error) {
    console.error("Ошибка загрузки:", error);
  }
}

Обработка ошибок в типизированном асинхронном коде



В TypeScript 4.0+ появилась возможность типизировать исключения с помощью защитников типа:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
class NetworkError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
    this.name = "NetworkError";
  }
}
 
class ValidationError extends Error {
  constructor(public fields: string[], message: string) {
    super(message);
    this.name = "ValidationError";
  }
}
 
async function fetchData(): Promise<UserData> {
  try {
    // реализация
  } catch (error: unknown) {
    if (error instanceof NetworkError) {
      // Обработка сетевой ошибки
      if (error.statusCode === 404) {
        return getDefaultUser(); // Возвращаем значение по умолчанию
      }
    }
    if (error instanceof ValidationError) {
      // Обработка ошибки валидации
      console.error("Некорректные поля:", error.fields);
    }
    throw error; // Пробрасываем остальные ошибки
  }
}

Композиция асинхронных операций



При объединении нескольких асинхронных вызовов сохраняйте типобезопасность:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface User {
  id: number;
  name: string;
}
 
interface Post {
  id: number;
  title: string;
  content: string;
}
 
// Получение связанных данных
async function getUserWithPosts(userId: number): Promise<{user: User; posts: Post[]}> {
  const [user, posts] = await Promise.all([
    fetchUser(userId),
    fetchUserPosts(userId)
  ]);
  
  return { user, posts };
}

Типизация сторонних библиотек и работа с .d.ts файлами



При использовании сторонних библиотек, особенно написанных на JavaScript, TypeScript требует определений типов.

Использование DefinitelyTyped



Большинство популярных библиотек имеют определения типов в репозитории DefinitelyTyped:

Bash
1
2
# Установка типов для библиотеки lodash
npm install --save-dev @types/lodash

Создание собственных определений типов



Если библиотека не имеет готовых типов, создайте файл определений:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
// types/untyped-library/index.d.ts
declare module 'untyped-library' {
  export function doSomething(input: string): number;
  
  export interface Options {
    timeout?: number;
    retries?: number;
  }
  
  export default function main(options?: Options): void;
}
Не забудьте добавить путь к определениям в tsconfig.json:

JSON
1
2
3
4
5
{
  "compilerOptions": {
    "typeRoots": ["./node_modules/@types", "./types"]
  }
}

Использование модуля declaration merging



Для существующих библиотек с неполными типами можно дополнить определения:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Расширение существующего модуля
import { Express } from 'express';
 
// Добавление пользовательских свойств
declare global {
  namespace Express {
    interface Request {
      user?: {
        id: string;
        role: string;
      };
    }
  }
}

Типизация React-компонентов и Redux-состояния



TypeScript особенно полезен при работе с популярными фреймворками.

Типизация функциональных компонентов React



TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
interface ButtonProps {
  text: string;
  onClick: () => void;
  variant?: 'primary' | 'secondary';
  disabled?: boolean;
}
 
// Используем React.FC с дженериком (хотя современный React склоняется к явному указанию типа возврата)
const Button: React.FC<ButtonProps> = ({ text, onClick, variant = 'primary', disabled = false }) => {
  return (
    <button
      className={`btn btn-${variant}`}
      onClick={onClick}
      disabled={disabled}
    >
      {text}
    </button>
  );
};
 
// Более современный подход
function Button({ text, onClick, variant = 'primary', disabled = false }: ButtonProps): JSX.Element {
  // Реализация
}

Типизация состояния Redux



TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// Определение типов действий
type ActionTypes = 
  | { type: 'ADD_TODO'; payload: { text: string } }
  | { type: 'TOGGLE_TODO'; payload: { id: number } }
  | { type: 'REMOVE_TODO'; payload: { id: number } };
 
// Определение типа состояния
interface Todo {
  id: number;
  text: string;
  completed: boolean;
}
 
interface TodoState {
  todos: Todo[];
  loading: boolean;
}
 
// Типизированный редьюсер
function todoReducer(state: TodoState, action: ActionTypes): TodoState {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, { id: Date.now(), text: action.payload.text, completed: false }]
      };
    // Другие случаи
    default:
      return state;
  }
}

CI/CD и автоматическая проверка типов в процессе разработки



Интеграция проверки типов в процесс непрерывной интеграции обеспечивает надежность кодовой базы.

Настройка проверки типов в CI-пайплайне



YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# .github/workflows/typescript-check.yml
name: TypeScript Check
 
on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]
 
jobs:
  type-check:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Use Node.js
        uses: actions/setup-node@v2
        with:
          node-version: '16'
      - name: Install dependencies
        run: npm ci
      - name: TypeScript check
        run: npx tsc --noEmit

Автоматическая генерация отчетов о типах



Для анализа покрытия типами можно настроить инструменты вроде type-coverage:

Bash
1
2
3
4
npm install --save-dev type-coverage
 
# Проверка покрытия типами
npx type-coverage --detail
Интеграция TypeScript в реальные проекты требует не только знания синтаксиса, но и понимания практических аспектов, включая производительность, интеграцию и процессы разработки. Благодаря правильному подходу система типов становится не препятствием, а мощным инструментом, ускоряющим разработку и повышающим качество кода.

Типизация в реальных проектах: практические аспекты



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

Организация типов в крупных проектах



В больших командах и сложных проектах организация типов становится критически важной для поддержания кодовой базы. Существует несколько проверенных временем подходов.

Монорепозитории и типизация между пакетами



Монорепозитории — популярное решение для крупных проектов, объединяющих несколько взаимосвязанных пакетов. TypeScript хорошо вписывается в такую архитектуру, но требует дополнительных настроек:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
// packages/common/src/types/index.ts
export interface User {
 id: string;
 name: string;
 email: string;
}
 
// packages/api/src/users/service.ts
import { User } from '@project/common';
 
export async function getUserById(id: string): Promise<User> {
 // Реализация
}
 
// packages/frontend/src/components/UserProfile.tsx
import { User } from '@project/common';
import { getUserById } from '@project/api';
 
interface Props {
 userId: string;
}
 
export function UserProfile({ userId }: Props) {
 const [user, setUser] = useState<User | null>(null);
 
 useEffect(() => {
   async function fetchUser() {
     try {
       const userData = await getUserById(userId);
       setUser(userData);
     } catch (error) {
       console.error('Failed to fetch user:', error);
     }
   }
   
   fetchUser();
 }, [userId]);
 
 // Отображение профиля
}
Для эфективной работы монорепозитория с TypeScript рекомендуется:

1. Настроить ссылки на проект (project references) в tsconfig.json:

JSON
1
2
3
4
5
6
7
8
9
10
11
// packages/api/tsconfig.json
{
 "compilerOptions": {
   "composite": true,
   "declaration": true,
   "outDir": "./dist"
 },
 "references": [
   { "path": "../common" }
 ]
}
2. Использовать инструменты для монорепозиториев, такие как Lerna, Nx или Turborepo, которые поддерживают компиляцию TypeScript с учётом зависимостей между пакетами.

Подход к совместному использованию типов



Существует три основных подхода к совместному использованию типов между частями приложения:

1. Централизованный подход — все типы определяются в едином месте:

TypeScript
1
2
3
4
5
6
src/
├── types/
│   ├── index.ts     # Реэкспорт всех типов
│   ├── user.ts      # Типы, связанные с пользователями
│   ├── product.ts   # Типы, связанные с продуктами
│   └── ...
2. Распределённый подход — типы определяются рядом с их использованием и экспортируются при необходимости:

TypeScript
1
2
3
4
5
6
7
8
9
src/
├── features/
│   ├── user/
│   │   ├── types.ts  # Типы пользователя
│   │   ├── api.ts
│   │   └── components/
│   └── product/
│       ├── types.ts  # Типы продукта
│       └── ...
3. Гибридный подход — общие типы централизованы, специфичные типы распределены:

TypeScript
1
2
3
4
5
6
7
8
src/
├── types/           # Общие типы
│   ├── common.ts
│   └── api.ts
├── features/
│   ├── user/
│   │   ├── types.ts  # Специфичные типы пользователя
│   │   └── ...
Выбор подхода зависит от размера команды, сложности проекта и предпочтений разработчиков. В крупных проектах гибридный подход часто оказывается наиболее эфективным.

Взаимодействие с бэкендом и типизация API



Одна из самых сложных задач в реальных приложениях — обеспечение согласованности типов между фронтендом и бэкендом. Существует несколько стратегий решения этой проблемы.

Генерация типов из схемы API



Для REST API можно генерировать типы из OpenAPI (Swagger) спецификации:

Bash
1
2
3
4
5
# Установка инструмента для генерации
npm install --save-dev openapi-typescript
 
# Генерация типов из спецификации
npx openapi-typescript https://api.example.com/swagger.json -o src/types/api.ts
Получется набор типов, которые можно использовать при работе с API:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import type { components } from '../types/api';
 
type User = components['schemas']['User'];
type CreateUserRequest = components['schemas']['CreateUserRequest'];
 
async function createUser(data: CreateUserRequest): Promise<User> {
 const response = await fetch('/api/users', {
   method: 'POST',
   headers: { 'Content-Type': 'application/json' },
   body: JSON.stringify(data)
 });
 
 if (!response.ok) {
   throw new Error(`API error: ${response.status}`);
 }
 
 return await response.json() as User;
}

Совместное использование типов между фронтендом и бэкендом



Если и фронтенд, и бэкенд написаны на TypeScript, можно создать общий пакет с типами:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// packages/shared-types/index.ts
export interface User {
 id: string;
 email: string;
 name: string;
 role: 'admin' | 'user';
 createdAt: string; // ISO date string
}
 
export interface CreateUserDto {
 email: string;
 name: string;
 password: string;
}
 
// Типы ответов API
export interface ApiResponse<T> {
 data: T;
 status: 'success' | 'error';
 message?: string;
}
Этот подход обеспечивает согласованность типов на всех уровнях приложения и позволяет избежать дублирования кода.

Эволюция типов и обратная совместимость



В реальных проектах типы, как и сам код, развиваются со временем. Управление изменениями типов требует особого внимания, особенно в публичных API и библиотеках.

Семантическое версионирование типов



При изменении типов в публичном API следует придерживаться принципов семантического версионирования:
Патч-версия (1.0.x): исправления, не меняющие API (улучшение документации типов, более точные типы без нарушения существующего кода).
Минорная версия (1.x.0): добавление новых возможностей без нарушения обратной совместимости (новые свойства с модификатором ?, новые функции).
Мажорная версия (x.0.0): изменения, нарушающие обратную совместимость (удаление свойств, изменение сигнатур функций).

Поддержка обратной совместимости



Существует несколько техник для поддержки обратной совместимости при изменении типов:

1. Опциональные свойства для новых полей:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
// Было
interface User {
 id: string;
 name: string;
}
 
// Стало (обратно совместимо)
interface User {
 id: string;
 name: string;
 email?: string; // Новое опциональное поле
}
2. Перегрузка функций для изменения сигнатуры:

TypeScript
1
2
3
4
5
6
7
8
9
// Было
function fetchUsers(query: string): Promise<User[]>;
 
// Стало (обратно совместимо)
function fetchUsers(query: string): Promise<User[]>;
function fetchUsers(query: string, options: FetchOptions): Promise<User[]>;
function fetchUsers(query: string, options?: FetchOptions): Promise<User[]> {
 // Реализация
}
3. Объединение типов для поддержки старых и новых версий:

TypeScript
1
2
3
4
5
6
7
// Было
type UserRole = 'admin' | 'user';
 
// Стало (обратно совместимо)
type LegacyUserRole = 'admin' | 'user';
type NewUserRole = 'admin' | 'user' | 'moderator';
type UserRole = LegacyUserRole | NewUserRole;

Маркировка устаревших типов



Для обозначения устаревших типов, которые планируется удалить в будущем, можно использовать JSDoc-комментарии:

TypeScript
1
2
3
4
5
6
7
8
9
10
/**
* @deprecated Use `NewUserInterface` instead. Will be removed in version 3.0.0.
*/
export interface User {
 // ...
}
 
export interface NewUserInterface {
 // ...
}
Современные IDE показывают предупреждения при использовании помеченных как устаревшие типов, что помогает разработчикам постепенно мигрировать на новые версии.

Тестирование типов



Проверка корректности типов — не менее важная часть процесса разработки, чем юнит-тестирование функциональности.

Статические тесты типов



TypeScript позволяет создавать статические тесты для проверки правильности определения типов:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// tests/types.test.ts
import { expectType } from 'tsd';
import { User, createUser } from '../src/user';
 
// Проверка, что функция возвращает правильный тип
expectType<Promise<User>>(createUser('John', 'john@example.com'));
 
// Проверка, что тип содержит ожидаемые свойства
type UserKeys = keyof User;
expectType<'id' | 'name' | 'email'>('' as unknown as UserKeys);
 
// Проверка, что тип не допускает неверные значения
// @ts-expect-error
const invalidUser: User = { name: 'John' }; // Отсутствует обязательное поле id
Эти тесты не выполняются во время выполнения, а проверяются компилятором TypeScript. Они помогают убедиться, что интерфейсы и типы работают так, как ожидается.

Проверка типобезопасности в CI/CD



Интеграция проверки типов в процесс непрерывной интеграции гарантирует, что все изменения кода соответствуют определённым типам:

YAML
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# .github/workflows/type-check.yml
name: Type Check
 
on:
 push:
   branches: [ main, development ]
 pull_request:
   branches: [ main ]
 
jobs:
 type-check:
   runs-on: ubuntu-latest
   steps:
     - uses: actions/checkout@v3
     - name: Setup Node.js
       uses: actions/setup-node@v3
       with:
         node-version: '16'
         cache: 'npm'
     - name: Install dependencies
       run: npm ci
     - name: Type check
       run: npm run type-check
     - name: Run type tests
       run: npm run test:types

Расширенные методы работы с типами в корпоративных проектах



В корпоративной среде, где кодовые базы могут достигать миллионов строк кода, эффективная работа с типами требует дополнительных инструментов и методик.

Автоматизация работы с типами



Для улучшения разработки можно использовать инструменты автоматизации:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// Автоматическая генерация типов из моделей базы данных
import { Model, DataTypes } from 'sequelize';
import { generateTypes } from 'sequelize-typescript-generator';
 
class UserModel extends Model {
 static init(sequelize) {
   super.init({
     id: {
       type: DataTypes.UUID,
       primaryKey: true
     },
     name: DataTypes.STRING,
     email: {
       type: DataTypes.STRING,
       unique: true
     }
   }, { sequelize });
 }
}
 
// Генерация типов из модели
generateTypes([UserModel], 'src/types/generated');

Типизация паттернов проектирования



Популярные паттерны проектирования также могут быть типизированы для повышения надёжности кода:

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Типизированный паттерн Наблюдатель (Observer)
interface Observer<T> {
 update(data: T): void;
}
 
class Subject<T> {
 private observers: Observer<T>[] = [];
 
 attach(observer: Observer<T>): void {
   if (!this.observers.includes(observer)) {
     this.observers.push(observer);
   }
 }
 
 detach(observer: Observer<T>): void {
   const index = this.observers.indexOf(observer);
   if (index !== -1) {
     this.observers.splice(index, 1);
   }
 }
 
 notify(data: T): void {
   for (const observer of this.observers) {
     observer.update(data);
   }
 }
}
 
// Использование
interface UserUpdateEvent {
 userId: string;
 field: 'name' | 'email';
 newValue: string;
}
 
class UserService extends Subject<UserUpdateEvent> {
 updateUserField(userId: string, field: 'name' | 'email', value: string): void {
   // Логика обновления
   
   // Уведомление наблюдателей
   this.notify({ userId, field, newValue: value });
 }
}
 
class NotificationService implements Observer<UserUpdateEvent> {
 update(data: UserUpdateEvent): void {
   console.log(`User ${data.userId} changed ${data.field} to ${data.newValue}`);
 }
}
 
// Настройка
const userService = new UserService();
const notificationService = new NotificationService();
userService.attach(notificationService);
 
// Использование
userService.updateUserField('user-123', 'name', 'John Doe');
Типизация паттернов проектирования делает код не только надёжнее, но и более понятным, так как типы служат дополнительной документацией.

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

Создать редактор радиосхем для MVC5, используя TypeScript
Ребята!) Нужна инфа как возможно создать редактор,используя typescript (js нежелательно),на mvc 5...

Разбиение скомилированного Typescript на файлы
В проекте имеется множество typescript файлов, которые компилируются в один js. Но часть скриптов...

Изучение TypeScript - советы
Нуждаюсь в срочном освоении TypeScript. Поделитесь ресурсами, пожалуйста. Можно на русском и...

Не понятен пример кода из спецификации TypeScript
Читаю про объектные типы в спецификации на странцие 13, но не понятно из описания как устроен и...

Передать свойство класса в анонимную функцию TypeScript
как передать значение свойства класса в анонимную функцию. например следующий код работает...

TypeScript "PreComputeCompileTypeScript" how to fix in project
Выскакивает ошибка Везде исправление данной ошибки идёт путём редактирования файла ...

Решение кольцевых зависимостей TypeScript + RequreJS
Всем привет, возникла проблема с кольцевыми зависимостями при использовании наследования в...

TypeScript | extends error
Собственно, сделал такой класс: class trueDate extends Date { constructor(date: string)...

Сохранение this в TypeScript
Доброго дня. Подскажите пожалуйста, как можно сохранить this класса так, чтобы можно было...

C# класс в TypeScript класс (перенос сущностей)
делается бекенд на C#, фронтенд на ts, общаются через REST API (http) сериализация обьектов...

не могу настроить react + typescript в webstorm. Есть люди кто это сделал?
Помогите, а то что то туплю уже и пробовал библиотеку скаченную подключать и ссылками. но так...

Лучшие видео ресурсы о программировании, angualr 2, typeScript, react и все сомое вкусное
Всем доброй поры времени. Я нашел новый для себя, и как не странно, вообще новый ресур, с видео...

Метки typescript
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 0
Комментарии
 
Новые блоги и статьи
Настройка гиперпараметров с помощью Grid Search и Random Search в Python
AI_Generated 15.05.2025
В машинном обучении существует фундаментальное разделение между параметрами и гиперпараметрами моделей. Если параметры – это те величины, которые алгоритм "изучает" непосредственно из данных (веса. . .
Сериализация и десериализация данных на Python
py-thonny 15.05.2025
Сериализация — это своего рода "замораживание" объектов. Вы берёте живой, динамический объект из памяти и превращаете его в статичную строку или поток байтов. А десериализация выполняет обратный. . .
Чем асинхронная логика (схемотехника) лучше тактируемой, как я думаю, что помимо энергоэффективности - ещё и безопасность.
Hrethgir 14.05.2025
Помимо огромного плюса в энергоэффективности, асинхронная логика - тотальный контроль над каждым совершённым тактом, а значит - безусловная безопасность, где безконтрольно не совершится ни одного. . .
Многопоточные приложения на C++
bytestream 14.05.2025
C++ всегда был языком, тесно работающим с железом, и потому особеннно эффективным для многопоточного программирования. Стандарт C++11 произвёл революцию, добавив в язык нативную поддержку потоков,. . .
Stack, Queue и Hashtable в C#
UnmanagedCoder 14.05.2025
Каждый опытный разработчик наверняка сталкивался с ситуацией, когда невинный на первый взгляд List<T> превращался в узкое горлышко всего приложения. Причина проста: универсальность – это прекрасно,. . .
Как использовать OAuth2 со Spring Security в Java
Javaican 14.05.2025
Протокол OAuth2 часто путают с механизмами аутентификации, хотя по сути это протокол авторизации. Представьте, что вместо передачи ключей от всего дома вашему другу, который пришёл полить цветы, вы. . .
Анализ текста на Python с NLTK и Spacy
AI_Generated 14.05.2025
NLTK, старожил в мире обработки естественного языка на Python, содержит богатейшую коллекцию алгоритмов и готовых моделей. Эта библиотека отлично подходит для образовательных целей и. . .
Реализация DI в PHP
Jason-Webb 13.05.2025
Когда я начинал писать свой первый крупный PHP-проект, моя архитектура напоминала запутаный клубок спагетти. Классы создавали другие классы внутри себя, зависимости жостко прописывались в коде, а о. . .
Обработка изображений в реальном времени на C# с OpenCV
stackOverflow 13.05.2025
Объединение библиотеки компьютерного зрения OpenCV с современным языком программирования C# создаёт симбиоз, который открывает доступ к впечатляющему набору возможностей. Ключевое преимущество этого. . .
POCO, ACE, Loki и другие продвинутые C++ библиотеки
NullReferenced 13.05.2025
В C++ разработки существует такое обилие библиотек, что порой кажется, будто ты заблудился в дремучем лесу. И среди этого многообразия POCO (Portable Components) – как маяк для тех, кто ищет. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru