Object, Array, Tuple¶
Пришло время рассмотреть такие типы данных как Object
и Array
, с которыми разработчики JavaScript уже хорошо знакомы. А также неизвестный им тип данных Tuple
, который, как мы скоро убедимся, не представляет собой ничего сложного.
Object (object) — ссылочный объектный тип¶
Ссылочный тип данных Object
является базовым для всех ссылочных типов в TypeScript.
Помимо того, что в TypeScript существует объектный тип Object
, представляющий одноименный конструктор из JavaScript, также существует тип object
, представляющий собой любое объектное значение. Поведение типа, указанного с помощью ключевого слова object
, и интерфейса Object
различаются.
Переменные, которым указан тип с помощью ключевого слова object
, не могут хранить значения примитивных типов, чьи идентификаторы (имена) начинаются со строчной буквы (number
, string
и т.д.). В отличие от них тип интерфейс Object
совместим с любым типом данных.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
По факту, тип, указанный как object
, соответствует чистому объекту, то есть не имеющему никаких признаков (даже унаследованных от типа Object
). В то время как значение, ограниченное типом Object
, будет включать все его признаки (методы hasOwnProperty()
и т.п.). При попытке обратиться к членам объекта, не задекларированным в интерфейсе Object
, возникнет ошибка. Напомним, что в случаях, когда тип нужно сократить до базового, сохранив при этом возможность обращения к специфичным (определенным пользователем) членам объекта, нужно использовать тип any
.
1 2 3 4 5 6 7 8 9 10 11 |
|
Тип интерфейса Object
идентичен по своей работе одноименному типу из JavaScript. Несмотря на то, что тип, указанный с помощью ключевого слова object
, имеет схожее название, его поведение отличается от типа интерфейса.
Array (type[]) ссылочный массивоподобный тип¶
Ссылочный тип данных Array
является типизированным спископодобным объектом, содержащим логику для работы с элементами.
Тип данных Array
указывается с помощью литерала массива, перед которым указывается тип данных type[]
.
Если при объявлении массива указать тип string[]
, то он сможет хранить только элементы, принадлежащие или совместимые с типом string
(например, null
, undefined
, literal type string
).
1 2 3 4 5 6 |
|
В случае неявного указания типа вывод типов самостоятельно укажет тип как string[]
.
1 |
|
Если требуется, чтобы массив хранил смешанные типы данных, то один из способов это сделать — указать тип объединение (Union
). Нужно обратить внимание на то, как трактуется тип данных Union
при указании его массиву. Может показаться, что, указав в качестве типа тип объединение Union
, массив (Elephant | Rhino | Gorilla)[]
может состоять только из какого-то одного перечисленного типа Elephant
, Rhino
или Gorilla
. Но это не совсем так. Правильная трактовка гласит, что каждый элемент массива может принадлежать к типу Elephant
или Rhino
, или Gorilla
. Другими словами, типом, к которому принадлежит массив, ограничивается не весь массив целиком, а каждый отдельно взятый его элемент.
1 2 3 4 5 6 7 8 9 |
|
Если для смешанного массива не указать тип явно, то вывод типов самостоятельно укажет все типы, которые хранятся в массиве. Более подробно эта тема будет рассмотрена в главе “Типизация - Вывод типов”.
В случае, если при создании экземпляра массива типы его элементов неизвестны, то следует указать в качестве типа тип any
.
1 2 3 4 5 |
|
Нужно стараться как можно реже использовать массивы со смешанными типами, а к массивам с типом any
нужно прибегать только в самых крайних случаях. Кроме того, как было рассказано в главе “Экскурс в типизацию - Совместимость типов на основе вариантности”, нужно крайне осторожно относиться к массивам, у которых входные типы являются ковариантными.
В случаях, требующих создания экземпляра массива с помощью оператора new
, необходимо прибегать к типу глобального обобщённого интерфейса Array<T>
. Обобщения будут рассмотрены чуть позднее, а пока нужно запомнить следующее. При попытке создать экземпляр массива путем вызова конструктора, операция завершится успехом в тех случаях, когда создаваемый массив будет инициализирован пустым либо с элементами одного типа данных. В случаях смешанного массива его тип необходимо конкретизировать явно с помощью параметра типа, заключенного в угловые скобки. Если сейчас это не понятно, не переживайте, в будущем это будет рассмотрено очень подробно.
1 2 3 4 5 6 7 8 9 10 |
|
В TypeScript поведение типа Array<T>
идентично поведению одноимённого типа из JavaScript.
Tuple ([T0, T1, …, Tn]) тип кортеж¶
Тип Tuple
(кортеж) описывает строгую последовательность множества типов, каждый из которых ограничивает элемент массива с аналогичным индексом. Простыми словами, кортеж задает уникальный тип для каждого элемента массива. Перечисляемые типы обрамляются в квадратные скобки, а их индексация, так же как у массива начинается с нуля - [T1, T2, T3]
. Типы элементов массива, выступающего в качестве значения, должны быть совместимы с типами обусловленных кортежем под аналогичными индексами.
Другими словами, если кортеж составляет последовательность типов string
и number
, то в качестве значения должен выступать массив, первый элемент которого совместим с типом string
, а второй с number
. В иных ситуациях неизбежно возникнет ошибка.
1 2 3 4 |
|
Длина массива-значения должна соответствовать количеству типов, указанных в Tuple
.
1 2 3 4 |
|
Но это правило не мешает добавить новые элементы после того, как массив был присвоен ссылке (ассоциирован со ссылкой). Но элементы, чьи индексы выходят за пределы установленные кортежем, обязаны иметь тип, совместимый с одним из перечисленных в этом кортеже.
1 2 3 4 5 6 7 8 9 10 |
|
Массив, который связан с типом кортежем, ничем не отличается от обычного, за исключением способа определения типа его элементов. При попытке присвоить элемент под индексом 0 переменной с типом string
, а элемент под индексом 1
переменной с типом number
, операции присваивания завершатся успехом. Но, несмотря на то, что элемент под индексом 2
хранит значение, принадлежащее к типу string
, оно не будет совместимо со string
. Дело в том, что элементы, чьи индексы выходят за пределы, установленные кортежем, принадлежат к типу объединение (Union
). Это означает, что элемент под индексом 2
принадлежит к типу string | number
, а это не то же самое, что тип string
.
1 2 3 4 5 6 7 |
|
Есть два варианта решения этой проблемы. Первый вариант, изменить тип переменной со string
на тип объединение string | number
, что ненадолго избавит от проблемы совместимости типов. Второй, более подходящий вариант, прибегнуть к приведению типов, который детально будет рассмотрен позднее.
В случае, если описание кортежа может навредить семантике кода, его можно поместить в описание псевдонима типа (type
).
1 2 3 4 |
|
Кроме того, тип кортеж можно указывать в аннотации остаточных параметров (...rest
).
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Помимо этого, типы, указанные в кортеже, могут быть помечены как необязательные с помощью необязательного модификатора ?
.
1 2 3 4 5 6 |
|
У кортежа, который включает типы, помеченные как необязательные, свойство длины принадлежит к типу объединения (Union
), состоящего из литеральных числовых типов.
1 2 3 4 5 6 7 |
|
Кроме того, для кортежа применим механизм распространения (spread
), который может быть указан в любой части определения типа. Но существуют два исключения. Во первых, определение типа кортежа может включать только одно распространение.
1 2 3 4 5 6 7 8 9 10 |
|
И во вторых, распространение не может быть указано перед необязательными типами.
1 2 3 4 5 6 7 |
|
В результате распространения, получается тип с логически предсказуемой последовательностью типов, определяющих кортеж.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Стоит заметить, что поскольку механизм распространения участвует в рекурсивном процессе формирования типа, способного значительно замедлять компилятор, установленно ограничение в размере 10000 итераций.
Механизм объявления множественного распространения (spread
) значительно упрощает аннотирование сигнатуры функции при реализации непростых сценариев, один из которых будет рассмотрен далее в главе (Массивоподобные readonly типы)[].
Еще несколько неочевидных моментов в логике кортежа связанны с выводом типов и будут рассмотрены в главе “Типизация - Вывод типов” (см реализацию функции concat).
Помимо этого семантику типов кортежей можно повышать за счет добавления им меток.
1 2 3 4 5 6 7 8 9 10 11 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
Поскольку метки являются исключительной частью синтаксиса TypeScript, они не имеют никакой силы в коде при деструктуризации массива, представленного типом кортежа.
1 2 3 |
|
Единственное правило, касающееся данного механизма, заключается в том, что кортеж, содержащий метки, не может содержать элементы, описанные только типами.
1 |
|
Напоследок стоит обратить внимание на тот факт, что тип переменной при присвоении ей инициализированного массива без явного указания типа, будет выведен как массив. Другими словами, вывод типа неспособен вывести тип кортеж.
1 |
|
Тип Tuple
является уникальным для TypeScript, в JavaScript подобного типа не существует.