Система типов в 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.