Эти подводные камни станут для вас открытием

Почему вступление? Эти подводные камни уже тратят достаточно времени — поехали!

Протестировано и подтверждено в TS V. 4.4.4

1. Надеюсь, вы никогда не увидите тип «никогда»

Не создавайте тип из двух разных интерфейсов.

Оставайтесь в безопасности с оператором &—, но не используйте оператор |—, иначе TypeScript ударит вас типом never.

В приведенном ниже примере убедитесь, что вы создаете объединение UserOrLog из User или Log. Затем вы хотите проверить буквальное объединение с оператором keyof — и вы получите фантастический тип never.

Почему это происходит? И как исправить.

Причина, по которой keyof Union никогда не приводит к результату, заключается в том, что keyof всегда возвращает доступные ключи типа.

В случае объединения это будут только общие ключи.

Поскольку интерфейсы User и Log не имеют общих ключей, используется тип never.

2. Вы можете легко подвергнуться жестокому обращению с помощью «|»

Мы с тобой прекрасно поняли смысл | , не так ли?

Нет, если вы используете его для типов, вы не получаете различия или, вместо этого вы получаете смесь обоих типов. Вот почему они назвали его union в штаб-квартире TypeScript.

Взгляните на пример, чтобы увидеть, как вы можете сделать Griffin из команды Lion | Bird. Переменнаяmixtureможет содержать и то, и другое, хотя кажется, что не должно.

Почему это происходит? И как это исправить.

Выражение типа Union A | B можно присвоить либо A, либо B. Он должен иметь свойства из A или B (или обоих).

Пересечение A & B, обязательные свойства как A, так и B.

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

3. Относитесь скептически, когда класс реализует интерфейс

Интерфейс со свойством двух возможных типов представляет собой чистую опасность в сочетании с классом.

Класс FishClass реализует интерфейс Fish и применяет значение 'Yes' типа string. Теперь в конструкторе вы решаете прикрепить значение true. Хотя в определении сказано, что вам разрешено, класс заранее заблокировал тип.

Обратного пути нет.

Почему это происходит? И как исправить.

Члены класса не контекстно типизированы типами, которые они намереваются реализовать или расширить.

Объявление extends или implements для class не влияет на тип экземпляра класса. Вместо этого он просто выполняет постфактумную проверку класса на соответствие суперклассу или интерфейсу. Поэтому, если ваш класс не реализует или не расширяет то, что вы объявили, вы получите предупреждение компилятора.

Но что касается результирующих типов, это то же самое, как если бы вы вообще удалили предложение extends или items. Нет, это никому не нравится.

Когда дело доходит до того, как выводятся члены. Поскольку свойство alive инициализируется значением string, равным "Yes", компилятор делает вывод, что оно имеет тип string. Позже, когда я назначу ему true, это будет ошибкой, потому что вы не можете назначить boolean на string. Обратите внимание, что string можно присвоить string | boolean, поэтому проверка FishClass implements Fish завершается успешно; вам разрешено сужать свойства подтипов.

Пока вам придется смириться с этим фактом и повторить объединение поля, чтобы исправить это обстоятельство:

4. Допускаются опечатки в свойствах

Опечатки могут закончиться многочасовым поиском, хорошо, что TypeScript проверяет это за нас, не так ли?

Взгляните на следующий пример. TypeScript делает дерьмо, когда вы используете необязательные свойства. Свойство userLvl не существует в интерфейсе User.

В строке 8 все нормально, а компилятор выдает ошибку. Но строка 12 заставляет меня плакать, потому что она разрешена и совершенно легальна — никаких претензий со стороны компилятора.

Почему это происходит? И как исправить.

Проблема в том, что по правилам ООП производный тип должен быть совместим с базовым типом (loggedIn и userLevel)

Причина, по которой эта избыточная проверка свойств не действует, заключается в том, как выполняется проверка типов для map. Во-первых, возвращаемый тип обратного вызова получается из литерала объекта как { loggedIn: boolean, userLvl: number }.

Этот тип используется в качестве возвращаемого типа для map, поэтому он возвращает Array<{loggedIn: boolean, userLvl: number}>. Затем он назначается User[], что разрешено первым правилом. Я нигде не назначал объектный литерал в месте, где ожидалось User.

Один из способов получить ошибку — не позволять машинописному тексту делать вывод о результате обратного вызова, переданного в map. Мы можем сделать это, добавив аннотацию к обратному вызову. Затем мы напрямую назначаем объектный литерал в позиции, где ожидается User:

5. Использование оператора Spread для завершения класса в случае сбоя

Общепринятой практикой является объединение объектов с оператором распространения.

Проверьте пример, чтобы стать свидетелем сбоя оператора распространения. Функция spawn принимает тип Ghost, определенный как class. Вы решили создать объект, распространив существующий объект Boo на booSpread и передав его функции spawn. Здесь начинаются трудные времена. Свойствоscareотсутствует.

Почему это происходит? И как исправить.

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

Предложение Rest/Spread Properties for ECMAScript (ES2018) добавило свойства распространения в object literals. Он копирует собственные перечисляемые свойства из предоставленного объекта в новый объект.

Поверхностное клонирование (за исключением прототипа) или слияние объектов теперь возможно с использованием более короткого синтаксиса, чем Object.assign().

Таким образом, вы просто «клонируете» Ghost. Все, что содержится в прототипе, например методы, игнорируется. Если вы используете Object.assign(), также будут скопированы методы.

6. Определите интерфейс и сопоставьте все с ним

Вы определяете интерфейс по причине, чтобы заставить кого-то придерживаться его.

Но вы легко можете обойти это обстоятельство, воспользовавшись map методом Array.prototype. Массив dobby имеет тип any и поэтому содержит все внутри, кроме определенных свойств Document.

Создание нового массива с именем myDocuments типа Document[] теперь должно помешать вам присвоить этому массиву что-либо, кроме типа Documentнет.

Посмотрите на строку 11, это должно быть недопустимо. Позвоните 9–1–1.

Почему это происходит? И как исправить.

Если вы решите использовать any в качестве типа, вы отказываетесь от проверки типа.

Это позволяет вам назначить массив случайного содержимого dobby и позволить значению пройти проверку во время компиляции.

Вы можете легко изменить any на unknown, чтобы получить безопасный аналог. Затем проверка типов снова включается и не позволяет присваивать dobby типизированным массивам myDocuments1 и myDocuments2.

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

Присоединяйтесь к Medium и узнавайте мои и миллионы других полезных историй:

https://arnoldcode.medium.com/membership

Get the 26 Cheatsheets and Study Only What You Truly Need to Land Your First Web Developer Job!