Система типов в TypeScript¶
Мы рассмотрели основные возможности системы типов TypeScript, когда обсуждали Почему TypeScript?. Ниже приводятся некоторые ключевые выводы из этого обсуждения, которые не нуждаются в дальнейшем объяснении:
- Система типов в TypeScript спроектирована быть необязательной, так чтобы ваш JavaScript был TypeScript.
- TypeScript не блокирует генерацию JavaScript при наличии ошибок типов, что позволяет вам постепенно обновлять JS до TS.
Теперь давайте начнем с синтаксиса системы типов TypeScript. Таким образом, вы можете сразу начать использовать эти описания в своем коде и увидеть преимущества. Это подготовит вас к углублению позже.
Основные описания¶
Как упоминалось ранее, типы описываются с использованием синтаксиса :TypeAnnotation. Все, что доступно в области объявления типа, может использоваться как описание типа.
В следующем примере демонстрируются описания типов для переменных, параметров функции и возвращаемых значений функции:
1 2 3 4 | |
Простые типы¶
Простые типы JavaScript хорошо представлены в системе типов TypeScript. Это означает обработку string, number, boolean как показано ниже:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
Массивы¶
TypeScript предоставляет особый синтаксис типов для массивов, чтобы вам было легче описывать и документировать свой код. Синтаксис в основном заключается в добавлении постфикса [] к любому валидному описанию типа (например, :boolean[]). Это позволяет вам безопасно выполнять любые манипуляции с массивами, которые вы обычно делаете, и защищает вас от ошибок, таких как присвоение неправильного типа. Это продемонстрировано ниже:
1 2 3 4 5 6 7 8 9 10 11 | |
Интерфейсы¶
Интерфейсы - это основной способ в TypeScript объединять описания нескольких типов в одно именованное описание. Рассмотрим следующий пример:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
Здесь мы соединили описание first: string + second: string в новое описание Name, которое обеспечивает проверку типов отдельных элементов. Интерфейсы имеют большой потенциал в TypeScript, и мы посвятим целый раздел тому, как вы можете использовать это в своих интересах.
Встроенное описания типа¶
Вместо создания нового interface вы можете описать все, что вы хотите встроенно, используя :{ /*Structure*/ }. Предыдущий пример представлен снова уже как встроенный тип:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Встроенные типы отлично подходят для быстрого однократного предоставления описания для чего-либо. Это избавляет вас от необходимости придумывать (потенциально плохое) имя типа. Однако, если вы обнаружите, что вставляете одно и то же встраиваемое описание типа несколько раз, неплохо было бы подумать о рефакторинге его в интерфейс (или псевдоним типа, описанный далее в этом разделе).
Специальные типы¶
Помимо рассмотренных простых типов, есть несколько типов, которые имеют особое значение в TypeScript. Это any, null, undefined, void.
any¶
Тип any занимает особое место в системе типов TypeScript. Он дает вам запасной выход из системы типов, чтобы заставить компилятор отстать от вас. any совместим с любыми типами в системе типов. Это означает, что что угодно может быть присвоено ему и он может быть присвоен чему угодно. Это продемонстрировано в примере ниже:
1 2 3 4 5 6 7 8 9 10 | |
Если вы переводите код JavaScript на TypeScript, вы вначале станете близкими друзьями с any. Однако не воспринимайте эту дружбу всерьёз, поскольку его использование означает, что вы сами должны обеспечить безопасность типа. Вы в основном говорите компилятору не делать никакого значимого статического анализа.
null или undefined¶
То, как они обрабатываются системой типов, зависит от флага компилятора strictNullChecks (мы рассмотрим этот флаг позже). Когда strictNullCheck:false, литералы JavaScript null и undefined эффективно обрабатываются системой типов как что-то наподобие any. Эти литералы могут быть назначены любому другому типу. Это продемонстрировано в следующем примере:
1 2 3 4 5 6 | |
:void¶
Используйте :void, чтобы описать, что функция ничего не возвращает:
1 2 3 | |
Дженерики¶
Многие алгоритмы и структуры данных в информатике не полагаются на текущий тип объекта. Тем не менее, вы все еще хотите наложить ограничения между различными переменными. Простым мини-примером является функция, которая принимает список элементов и возвращает перевернутый список элементов. Здесь есть ограничение между тем, что передается функции, и тем, что возвращается функцией:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | |
Здесь вы по сути говорите, что функция reverse принимает массив (items: T[]) элементов какого-то типа T (обратите внимание на параметр типа в reverse<T>) и возвращает массив элементов типа T (обратите внимание : T[]). Поскольку функция reverse возвращает элементы того же типа, что и приняла, TypeScript знает, что переменная reversed также имеет тип number[] и обеспечит вам защиту типа. Точно так же, если вы передаете массив string[] в функцию reverse, возвращаемый результат также является массивом string[], и вы получаете защиту типов, аналогичную показанной ниже:
1 2 3 4 | |
На самом деле JavaScript массивы уже имеют функцию .reverse, а TypeScript фактически использует дженерики для определения её структуры:
1 2 3 4 | |
Это означает, что вы получаете защиту типов при вызове .reverse для любого массива, как показано ниже:
1 2 3 4 | |
Мы обсудим больше интерфейс Array<T> позже, когда расскажем про lib.d.ts в разделе Объявления окружения.
Тип объединения¶
Как правило, в JavaScript вы хотите, чтобы свойство было одним из нескольких типов, например string или number. Здесь пригодится тип объединения (обозначаемый через | в описании типа, например, string|number). Обычный вариант использования - это функция, которая может принимать одиночный элемент или массив элементов, например:
1 2 3 4 5 6 7 8 9 10 | |
Тип пересечения¶
extend - это очень распространенный паттерн в JavaScript, где вы берете два элемента и создаете новый, который имеет функции обоих этих объектов. Тип пересечения позволяет вам безопасно использовать этот паттерн, как показано ниже:
1 2 3 4 5 6 7 8 9 | |
Тип кортежа¶
В JavaScript нет поддержки кортежей. Разработчики обычно используют массив в качестве кортежа. Но зато система типов TypeScript поддерживает кортежи. Кортежи могут быть описаны с помощью : [typeofmember1, typeofmember2] и т.д. Кортеж может иметь любое количество элементов. Кортежи демонстрируются в следующем примере:
1 2 3 4 5 6 7 | |
В сочетании с поддержкой деструктуризации в TypeScript, кортежи чувствуют себя довольно прекрасно, несмотря на то, что под ними расположены простые массивы:
1 2 3 4 | |
Псевдоним типа¶
TypeScript предоставляет удобный синтаксис для указания имен описания типов, которые вы хотели бы использовать более чем в одном месте. Псевдонимы создаются с использованием синтаксиса type SomeName = someValidTypeAnnotation. Пример демонстрируется ниже:
1 2 3 4 5 6 7 8 9 | |
В отличие от interface, вы можете дать псевдоним типа буквально для описания любого типа (полезно для таких вещей, как типы объединения и пересечения). Вот еще несколько примеров, чтобы вы познакомились с синтаксисом:
1 2 3 | |
СОВЕТ: Если вам нужны иерархии описаний типа, используйте
interface. Их можно использовать сimplementsиextendsСОВЕТ: используйте псевдоним типа для более простых структур объектов (например,
Coordinates), просто чтобы дать им семантическое имя. Также, когда вы хотите дать семантические имена для типов объединения или пересечения, псевдонимы типов - это верный способ.
Заключение¶
Теперь, когда вы можете начать описывать большую часть своего кода JavaScript, мы можем перейти к мельчайшим подробностям всей мощи, доступной в системе типов TypeScript.