TypeScript / 39 минут

Типы и вывод TypeScript

Добавляйте статические контракты без потери вывода типов.

В процессе Теория: впереди Практика: впереди Тест: впереди
01 / 12
После этой главы
  • Добавляйте статические контракты без потери вывода типов.
  • Применить тему в задаче «Типы и вывод TypeScript» и объяснить граничные случаи.

Около 9 минут на чтение и разбор примеров. Практика — отдельно, в своём темпе.

Проверка до выполнения

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

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

TypeScript
function amount(price: number, quantity: number): number {
  return price * quantity;
}
const total = amount(120, 3);
// amount('120', 3); // ошибка статической проверки

Вывод типов и разумные аннотации

Не каждое объявление требует явного типа. TypeScript часто выводит его из начального значения и контекста. let count = 0 получает тип number, а функция с очевидным возвратом может получить выведенный тип результата. Аннотации особенно полезны для публичных контрактов, сложных структур и случаев, где желаемая ширина типа отличается от первоначального значения.

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

TypeScript
let completed = 0; // number
const track = 'javascript'; // литеральный тип
const scores: number[] = [];
scores.push(completed);

Примитивы, массивы и объектные формы

Используйте string, number и boolean в обычных типах, а не объектные обёртки String и Number. Массив чисел можно записать как number[] или Array<number>. Объектная форма перечисляет нужные поля и их типы. Наличие type для формы не создаёт объект во время исполнения: значения по-прежнему строятся обычным JavaScript-кодом.

TypeScript
type Product = { title: string; price: number };
const product: Product = { title: 'Курс', price: 0 };
function label(item: Product): string {
  return `${item.title}: ${item.price}`;
}

TypeScript в основном структурно сравнивает формы. Если объект имеет подходящие свойства, он может соответствовать контракту независимо от места объявления. Это не означает, что лишние поля всегда принимаются в любой записи: проверка свежих объектных литералов имеет дополнительные правила. Главное — не путать совместимость типов с runtime-фильтрацией полей.

any, unknown и отсутствие

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

При strictNullChecks null и undefined не считаются автоматически подходящими для любого string или объекта. Если отсутствие допустимо, его включают в тип и обрабатывают. Это вынуждает явно подумать о пустом результате поиска или необязательном поле, вместо случайного падения при чтении свойства. Оператор ! может подавить предупреждение, но не создаёт отсутствующее значение.

TypeScript
function lengthOrZero(value: string | null): number {
  return value === null ? 0 : value.length;
}

Утверждение типа не является проверкой

Запись value as User сообщает компилятору, как рассматривать значение, но не запускает валидатор. Если сервер вернул другое тело, свойства останутся другими независимо от утверждения. Аналогично interface не проверяет HTTP-ввод самостоятельно. Разделение статической и runtime-проверки — одна из главных идей курса, а не редкое исключение.

TypeScript
const raw: unknown = JSON.parse('{"price":"ошибка"}');
// Утверждение не превращает строку в число:
const claimed = raw as { price: number };
// В runtime claimed.price всё ещё строка.

Компилятор может показывать ошибку, но конкретный инструмент сборки иногда всё равно генерирует JavaScript, если так настроен. Для надёжной проверки в проекте явно запускают tsc --noEmit или другой обязательный typecheck. Наличие расширения .ts не доказывает, что проверка выполнялась и завершилась успешно.

Практика числового контракта

В задаче нужно объявить функцию с числовыми price и quantity и вернуть произведение. Правильный тип результата — number, а не отформатированная строка с валютой. Проверьте ноль и дробную цену, но помните, что TypeScript не меняет двоичную арифметику JavaScript. Ограничения точности остаются теми же.

Попробуйте намеренно вернуть строку из функции и прочитайте сообщение компилятора. Затем замените параметр на any и посмотрите, какие ошибки перестали обнаруживаться. Для самостоятельного продолжения вынесите Product и расчёт стоимости в отдельный модуль, сохранив публичную сигнатуру. Цель — увидеть, как проверяемый контракт помогает изменять реализацию без случайного изменения поведения потребителей.

Загружаем редактор…
Следующая темаUnion и сужение типов

Музыка: Reverie — Scott Buckley · CC BY 4.0