Frontend

TypeScript для тех кто устал от неожиданных undefined

TypeScript для тех кто устал от неожиданных undefined

 

Если вы когда-либо просыпались в три часа ночи от уведомления в Sentry о том, что ваш продакшн «упал» из-за ошибки TypeError: Cannot read property 'id' of undefined, то эта статья для вас.

Для многих JavaScript — это прекрасный, гибкий, но абсолютно непредсказуемый инструмент. Мы привыкли к тому, что переменная может быть числом, строкой, объектом, а в самый неподходящий момент — превратиться в undefined или null, обрушив всё приложение. В индустрии это называют «динамической типизацией», но на практике это часто ощущается как игра в рулетку.

TypeScript пришел не для того, чтобы усложнить нам жизнь лишним синтаксисом. Он пришел, чтобы перенести поиск ошибок с этапа «пользователь нажал кнопку» на этап «я нажал Ctrl+S».

Почему JavaScript нас предает?

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

В больших проектах, где над кодом работают 5–10 человек, эта проблема масштабируется экспоненциально. Вы меняете структуру объекта в одном модуле, а в другом — в каком-нибудь забытом компоненте на 14-й странице — всё ломается, потому что там кто-то полагался на старую структуру.

TypeScript как страховой полис

TypeScript — это не новый язык, а «надстройка» (суперсет) над JavaScript. Его главная задача — добавить статическую типизацию. Это значит, что мы описываем форму наших данных заранее.

Когда вы четко говорите: «Здесь будет объект с полем username типа string», IDE начинает работать на вас. Как только вы попытаетесь обратиться к user.name вместо user.username, редактор подсветит это красным. Вам не нужно запускать код и ждать ошибки в консоли — вы видите проблему в момент написания.

Борьба с undefined: главный арсенал

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

1. Strict Null Checks: Режим «Без пощады»

Первое, что нужно сделать любому, кто переходит на TypeScript — включить опцию strictNullChecks в tsconfig.json. Без неё TS позволяет присваивать null или undefined любой переменной. С ней — нет.

Теперь, если вы объявили переменную как User, она обязана быть пользователем. Если она может отсутствовать, вы обязаны явно указать это: User | undefined. Это заставляет вас думать о крайних случаях еще до того, как вы написали бизнес-логику.

2. Union Types и защита типов (Type Guarding)

Когда мы помечаем тип как User | undefined, TypeScript больше не позволит нам просто так вызвать user.getName(). Он скажет: «Стоп, а что если здесь undefined?».

Чтобы «успокоить» компилятор, мы используем механизмы защиты:

  1. Простая проверка на истинность: if (user) { ... } — самый простой способ.
  2. Optional Chaining (Опциональная цепочка): Оператор ?. позволяет безопасно обращаться к вложенным свойствам. Вместо огромных цепочек проверок if (user && user.address && user.address.city), мы пишем user?.address?.city. Если любой элемент в цепочке окажется null или undefined, выражение просто вернет undefined, не обрушив приложение.
  3. Nullish Coalescing (Оператор нулевого слияния): Оператор ?? позволяет задать значение по умолчанию. Например: const name = user?.name ?? 'Аноним'. Это гораздо надежнее, чем старый добрый ||, который мог заменить на «Анонима» даже пустое число 0 или пустую строку, что часто приводило к другим багам.

Как правильно проектировать типы, чтобы не сойти с ума

Многие новички совершают ошибку, пытаясь типизировать абсолютно всё с избыточной строгостью или, наоборот, используя везде тип any.

any — это «черный ход», который отключает проверку типов. Использование any превращает TypeScript обратно в JavaScript. Если вы пишете any, вы говорите компилятору: «Я не знаю, что здесь, и мне всё равно». Это прямой путь к тем самым undefined, от которых мы бежим.

Правильный подход к типизации:

  1. Используйте Интерфейсы (interface) для структур данных. Опишите API-ответы, модели пользователей, настройки приложения. Это создает «контракт», который соблюдают все части системы.
  2. Используйте Типы (type) для объединений и алиасов. Например, статус заказа: type OrderStatus = 'pending' | 'shipped' | 'delivered'. Теперь вы не сможете передать туда строку 'sent', потому что её нет в списке допустимых значений.
  3. Избегайте принудительного приведения типов (as User). Оператор as — это способ сказать: «Верь мне, я знаю, что здесь User». Но TS вам верит на слово, а в рантайме там может оказаться что угодно. По возможности используйте Type Guards (функции-проверки).

Практический профит: что меняется в процессах

Переход на TS меняет не только код, но и то, как вы работаете с командой:

  1. Самодокументированный код. Вам больше не нужно писать в комментариях /** @param {Object} user - User object with id and name */. Тип user: User говорит об этом лучше любого комментария.
  2. Безопасный рефакторинг. Хотите переименовать поле userId в id во всем проекте? В JS это было бы поиском и заменой по всему проекту с молитвой о том, что вы ничего не пропустили. В TS вы переименовываете поле в интерфейсе, и IDE автоматически меняет его везде, либо подсвечивает все места, где теперь возникла ошибка.
  3. Ускорение онбординга. Новый разработчик в команде просто открывает определение типа и сразу видит, какие данные приходят из API и какие методы доступны у объекта. Ему не нужно «прокликивать» весь путь выполнения кода, чтобы понять структуру данных.

Заключение: стоит ли оно того?

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

TypeScript не делает вас идеальным программистом, но он работает как опытный коллега, который стоит за плечом и тихо говорит: «Слушай, а ты уверен, что здесь точно будет объект? А если API вернет ошибку?».

В итоге количество runtime-ошибок сокращается в разы, а уверенность в каждом коммите растет. Переход на TS — это переход от культуры «надеюсь, оно заработает» к культуре «я знаю, что оно работает». Если вы устали от неопределенности и хаоса в типах — самое время перестать бороться с undefined вручную и доверить эту работу инструментам.