HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>В этом уроке мы разберем связь между типами, которая выстраивается в иерархию.</p>
1 <p>В этом уроке мы разберем связь между типами, которая выстраивается в иерархию.</p>
2 <h2>Типы как подмножества</h2>
2 <h2>Типы как подмножества</h2>
3 <p>Рассмотрим пример ошибки<em>Type X is not assignable to type Y</em>в функции для сортировки элементов. Допустим, у нас уже написана функция sort. И чтобы описать только ее типы, воспользуемся ключевым словом declare:</p>
3 <p>Рассмотрим пример ошибки<em>Type X is not assignable to type Y</em>в функции для сортировки элементов. Допустим, у нас уже написана функция sort. И чтобы описать только ее типы, воспользуемся ключевым словом declare:</p>
4 <p>Проверка типов выдала ошибку: объединение литеральных типов 0 | 1 | -1 не совместимо с типом number. Можно подумать, что система типов ошибается, и стоит использовать any. Но если мы подумаем о литеральных числовых типах как о подмножествах number, все становится логично.</p>
4 <p>Проверка типов выдала ошибку: объединение литеральных типов 0 | 1 | -1 не совместимо с типом number. Можно подумать, что система типов ошибается, и стоит использовать any. Но если мы подумаем о литеральных числовых типах как о подмножествах number, все становится логично.</p>
5 <p>Здесь отчетливо просматривается связь типов с теорией множеств. Множество A является подмножеством B, если любой элемент, который принадлежит A, также принадлежит B. Так мы получаем связи между типами, которые выстраиваются в иерархию типов. Это помогает понять, возможно ли присвоить переменную одного типа переменной другого типа.</p>
5 <p>Здесь отчетливо просматривается связь типов с теорией множеств. Множество A является подмножеством B, если любой элемент, который принадлежит A, также принадлежит B. Так мы получаем связи между типами, которые выстраиваются в иерархию типов. Это помогает понять, возможно ли присвоить переменную одного типа переменной другого типа.</p>
6 <h2>Литеральные типы</h2>
6 <h2>Литеральные типы</h2>
7 <p>Напомним, что литеральные типы существуют для четырех типов данных:</p>
7 <p>Напомним, что литеральные типы существуют для четырех типов данных:</p>
8 <ul><li>boolean</li>
8 <ul><li>boolean</li>
9 <li>string</li>
9 <li>string</li>
10 <li>number</li>
10 <li>number</li>
11 <li>BigInt</li>
11 <li>BigInt</li>
12 </ul><p>В итоге любой литеральный тип можно присвоить переменной соответствующего типа:</p>
12 </ul><p>В итоге любой литеральный тип можно присвоить переменной соответствующего типа:</p>
13 <p>Здесь 2 используется как литеральный тип, который представляет собой множество из одного элемента - двойки.</p>
13 <p>Здесь 2 используется как литеральный тип, который представляет собой множество из одного элемента - двойки.</p>
14 <p>Анализатор успешно пропустил присваивание литерального типа числа к number, но литеральный boolean тип мы уже не смогли присвоить. Чтобы решить эту проблему, можно использовать объединение типов number | boolean. Но если мы не уверены, что может быть присвоено, нам пришлось бы делать объединение с потенциально огромным числом типов.</p>
14 <p>Анализатор успешно пропустил присваивание литерального типа числа к number, но литеральный boolean тип мы уже не смогли присвоить. Чтобы решить эту проблему, можно использовать объединение типов number | boolean. Но если мы не уверены, что может быть присвоено, нам пришлось бы делать объединение с потенциально огромным числом типов.</p>
15 <p>В этом случае нам на помощь приходит тип unknown.</p>
15 <p>В этом случае нам на помощь приходит тип unknown.</p>
16 <h2>Тип unknown</h2>
16 <h2>Тип unknown</h2>
17 <p>Тип unknown - это надмножество всех доступных типов. Он позволяет присвоить переменной значение произвольного типа:</p>
17 <p>Тип unknown - это надмножество всех доступных типов. Он позволяет присвоить переменной значение произвольного типа:</p>
18 <p>Может показаться, что тип unknown работает так же, как any. Однако между ними есть различие. Тип any по сути отключает проверку типов и позволяет выполнять любые операции со значением, например, обращаться к свойствам переменной. Тип unknown запрещает это и требует предварительной проверки типа переменной, либо приведения к нужному типу.</p>
18 <p>Может показаться, что тип unknown работает так же, как any. Однако между ними есть различие. Тип any по сути отключает проверку типов и позволяет выполнять любые операции со значением, например, обращаться к свойствам переменной. Тип unknown запрещает это и требует предварительной проверки типа переменной, либо приведения к нужному типу.</p>
19 <p>Далее разберем случай, когда нам не нужно присваивать переменной никакого значения.</p>
19 <p>Далее разберем случай, когда нам не нужно присваивать переменной никакого значения.</p>
20 <h2>Тип never</h2>
20 <h2>Тип never</h2>
21 <p>Иногда на практике нужно быть уверенным, что переменной не будет присвоено никакого значения. Это можно реализовать с помощью типа never:</p>
21 <p>Иногда на практике нужно быть уверенным, что переменной не будет присвоено никакого значения. Это можно реализовать с помощью типа never:</p>
22 <h2>Множества типов</h2>
22 <h2>Множества типов</h2>
23 <p>Из текущих знаний мы можем составить следующую картинку множеств типов TypeScript:</p>
23 <p>Из текущих знаний мы можем составить следующую картинку множеств типов TypeScript:</p>
24 <p>В множество number также входят все объединения литеральных типов чисел, а в множество string - литеральных строк:</p>
24 <p>В множество number также входят все объединения литеральных типов чисел, а в множество string - литеральных строк:</p>
25 <p>Такое подмножество типов называют подтипом, а само множество супертипом.</p>
25 <p>Такое подмножество типов называют подтипом, а само множество супертипом.</p>
26 <p>Взаимосвязи подтипов и супертипов - ключевая концепция любого статически типизированного языка. Они образуют иерархию типов. Это становится особо важно, когда мы хотим привести один тип к другому.</p>
26 <p>Взаимосвязи подтипов и супертипов - ключевая концепция любого статически типизированного языка. Они образуют иерархию типов. Это становится особо важно, когда мы хотим привести один тип к другому.</p>
27 <h2>Приведение типов</h2>
27 <h2>Приведение типов</h2>
28 <p>Рассмотрим различные варианты приведения типов:</p>
28 <p>Рассмотрим различные варианты приведения типов:</p>
29 <p>Когда мы присваиваем значение в переменную или передаем аргументы в функцию, TypeScript пытается сделать восходящее приведение - от подтипа к базовому. Также можно явно задать восходящее приведение. Мы уже пользовались этой возможностью, чтобы проверить, возможно ли привести один тип к другому или указать явно, переменную какого типа мы ожидаем.</p>
29 <p>Когда мы присваиваем значение в переменную или передаем аргументы в функцию, TypeScript пытается сделать восходящее приведение - от подтипа к базовому. Также можно явно задать восходящее приведение. Мы уже пользовались этой возможностью, чтобы проверить, возможно ли привести один тип к другому или указать явно, переменную какого типа мы ожидаем.</p>
30 <p>Приведение базового типа к подтипу делается явно с помощью as. При таком поведении TypeScript принимает приведение типов за истину. В некоторых случаях это может привести к ошибке. Поэтому нисходящее приведение считается небезопасным. К такому коду нужно пристально присмотреться.</p>
30 <p>Приведение базового типа к подтипу делается явно с помощью as. При таком поведении TypeScript принимает приведение типов за истину. В некоторых случаях это может привести к ошибке. Поэтому нисходящее приведение считается небезопасным. К такому коду нужно пристально присмотреться.</p>
31 <p>Разберем еще один пример:</p>
31 <p>Разберем еще один пример:</p>
32 <p>Здесь компилятор определяет переменной args как тип number[] - массив с любым количеством числовых элементов. Компилятор расширил возможные значения в массиве, несмотря на то, что мы указали всего два элемента в массиве. Это и есть неявное восходящее приведение типа, когда компилятор приводит к более общему типу.</p>
32 <p>Здесь компилятор определяет переменной args как тип number[] - массив с любым количеством числовых элементов. Компилятор расширил возможные значения в массиве, несмотря на то, что мы указали всего два элемента в массиве. Это и есть неявное восходящее приведение типа, когда компилятор приводит к более общему типу.</p>
33 <p>По этой причине возникает ошибка, потому что метод Math.atan2() ожидает два аргумента, а тип переменной args может содержать любое количество элементов. Исправим это с помощью as:</p>
33 <p>По этой причине возникает ошибка, потому что метод Math.atan2() ожидает два аргумента, а тип переменной args может содержать любое количество элементов. Исправим это с помощью as:</p>
34 <p>Теперь компилятор определяет тип для переменной args как литеральный тип [8, 5]. Хоть он и является множеством типа number[], но это уже более строгий тип, который является массивом из двух конкретных чисел, поэтому ошибки не будет. Такое приведение называется нисходящим, потому что мы от более широкого типа приводим к более узкому типу, содержащему меньшее количество возможных значений.</p>
34 <p>Теперь компилятор определяет тип для переменной args как литеральный тип [8, 5]. Хоть он и является множеством типа number[], но это уже более строгий тип, который является массивом из двух конкретных чисел, поэтому ошибки не будет. Такое приведение называется нисходящим, потому что мы от более широкого типа приводим к более узкому типу, содержащему меньшее количество возможных значений.</p>