Основы Typescript
Теория: Иерархия типов
В этом уроке мы разберем связь между типами, которая выстраивается в иерархию.
Типы как подмножества
Рассмотрим пример ошибки Type X is not assignable to type Y в функции для сортировки элементов. Допустим, у нас уже написана функция sort. И чтобы описать только ее типы, воспользуемся ключевым словом declare:
Проверка типов выдала ошибку: объединение литеральных типов 0 | 1 | -1 не совместимо с типом number. Можно подумать, что система типов ошибается, и стоит использовать any. Но если мы подумаем о литеральных числовых типах как о подмножествах number, все становится логично.
Здесь отчетливо просматривается связь типов с теорией множеств. Множество A является подмножеством B, если любой элемент, который принадлежит A, также принадлежит B. Так мы получаем связи между типами, которые выстраиваются в иерархию типов. Это помогает понять, возможно ли присвоить переменную одного типа переменной другого типа.
Литеральные типы
Напомним, что литеральные типы существуют для четырех типов данных:
booleanstringnumberBigInt
В итоге любой литеральный тип можно присвоить переменной соответствующего типа:
Здесь 2 используется как литеральный тип, который представляет собой множество из одного элемента — двойки.
Анализатор успешно пропустил присваивание литерального типа числа к number, но литеральный boolean тип мы уже не смогли присвоить. Чтобы решить эту проблему, можно использовать объединение типов number | boolean. Но если мы не уверены, что может быть присвоено, нам пришлось бы делать объединение с потенциально огромным числом типов.
В этом случае нам на помощь приходит тип unknown.
Тип unknown
Тип unknown — это надмножество всех доступных типов. Он позволяет присвоить переменной значение произвольного типа:
Может показаться, что тип unknown работает так же, как any. Однако между ними есть различие. Тип any по сути отключает проверку типов и позволяет выполнять любые операции со значением, например, обращаться к свойствам переменной. Тип unknown запрещает это и требует предварительной проверки типа переменной, либо приведения к нужному типу.
Далее разберем случай, когда нам не нужно присваивать переменной никакого значения.
Тип never
Иногда на практике нужно быть уверенным, что переменной не будет присвоено никакого значения. Это можно реализовать с помощью типа never:
Множества типов
Из текущих знаний мы можем составить следующую картинку множеств типов TypeScript:

В множество number также входят все объединения литеральных типов чисел, а в множество string — литеральных строк:
Такое подмножество типов называют подтипом, а само множество супертипом.
Взаимосвязи подтипов и супертипов — ключевая концепция любого статически типизированного языка. Они образуют иерархию типов. Это становится особо важно, когда мы хотим привести один тип к другому.
Приведение типов
Рассмотрим различные варианты приведения типов:
Когда мы присваиваем значение в переменную или передаем аргументы в функцию, TypeScript пытается сделать восходящее приведение — от подтипа к базовому. Также можно явно задать восходящее приведение. Мы уже пользовались этой возможностью, чтобы проверить, возможно ли привести один тип к другому или указать явно, переменную какого типа мы ожидаем.
Приведение базового типа к подтипу делается явно с помощью as. При таком поведении TypeScript принимает приведение типов за истину. В некоторых случаях это может привести к ошибке. Поэтому нисходящее приведение считается небезопасным. К такому коду нужно пристально присмотреться.
Разберем еще один пример:
Здесь компилятор определяет переменной args как тип number[] — массив с любым количеством числовых элементов. Компилятор расширил возможные значения в массиве, несмотря на то, что мы указали всего два элемента в массиве. Это и есть неявное восходящее приведение типа, когда компилятор приводит к более общему типу.
По этой причине возникает ошибка, потому что метод Math.atan2() ожидает два аргумента, а тип переменной args может содержать любое количество элементов. Исправим это с помощью as:
Теперь компилятор определяет тип для переменной args как литеральный тип [8, 5]. Хоть он и является множеством типа number[], но это уже более строгий тип, который является массивом из двух конкретных чисел, поэтому ошибки не будет. Такое приведение называется нисходящим, потому что мы от более широкого типа приводим к более узкому типу, содержащему меньшее количество возможных значений.





