Русский
Русский
English
Статистика
Реклама

ТайпСкрип Ох уж эта весёлая система типов

Здравствуйте, меня зовут Дмитрий Карловский и недавно я, вместе с Артуром Мукминовым, проводил воркшоп, где показывал как разрабатывать сложные типофункции через тестирование. Это 2 часа сурового программирования на типах. Так что в качестве тизера, ловите разбор курьёзов тайпскриптовой системы типов.



Отношения это сложно


Проверить является ли один тип подтипом другого очень просто, используя типотернарник:


type IsAExtendsB = A extends B ? true : false

На воркшопе мы разработали типофункцию Classify, принимающую 2 типа и возвращающую одно из 4 возможных значений:


  • [ A, '<:', B ] A является строгим подтипом B.
  • [ A, ':>', B ] B является строгим подтипом A.
  • [ A, '==', B ] Оба типа являются подтипами друг друга (но не обязательно являются одинаковыми типами).
  • [ A, '!=', B ] Ни один тип не является подтипом другого.

Кроме того, мы запилили типофункции Equal и Assert, позволяющие сравнивать типы на равенство, независимо от того, считает ли компилятор два разных типа подтипами друг друга или нет. Assert при этом ещё и валит проверку типов, если типы вдруг не совпали.


Всё есть объекты! Но это не точно..


Ну и первый же прикол Object и object это определённо разные типы, ибо примитивные типы являются подтипами первого, но не второго:


type boolean_is_Object = Assert<    boolean extends Object ? true : false,    true>type boolean_is_not_object = Assert<    boolean extends object ? true : false,    false>

Однако, если мы сравним их, то выяснится, что они являются подтипами друг друга:


type Object_vs_object = Assert<    Classify< Object, object >,    [ Object, '==', object ]>

То есть отношение подтипизации в тайпскрипте не является транзитивным: если один тип (например, boolean) является подтипом другого (например, Object), а другой третьего (например, object), то первый вовсе не обязательно является подтипом третьего это надо проверять отдельно.


На диаграмме, все объектные типы раскрашены в голубой цвет. Они являются подтипами как Object, так и object.


Разные типы перечислений типов


Раз уж мы заговорили про булевый тип, то нельзя не упомянуть, что он ничто иное, как кроткий алиас для объединения пары литеральных типов:


type boolean_is_true_or_false = Assert<    boolean,    true | false>

С числовыми перечислениями всё в принципе аналогично:


enum FL4 { Absurd, False, True, Unknown }type FL4_is_union = Assert<    FL4,    | FL4.Absurd | FL4.False | FL4.True | FL4.Unknown>

И состоят они вроде бы из чисел (даже не литералов):


type Absurd_is_number = Assert<    Classify< FL4.Absurd, number >,    [ FL4.Absurd, '==', number ]>

Но тут тайпскрипту внезапно сносит крышу:


type Absurd_is_never_wtf = Assert<    Classify< FL4.Absurd, 0 >,    [ never, '<:', 0 ]>

Эй, тайпскрипт, ты куда первый тип потерял? Верни, где взял!


type One_is_never_wtf = Assert<    Classify< FL4.Absurd, 1 >,    [ FL4.Absurd, ':>', never ]>

Вот, спасибо, совсем другое дело!


По всей видимости связано это с тем, что значения перечислений это не не простые литералы, а уникальные:


enum FL3 { Absurd, False, True }type Absurd_is_not_Absurd = Assert<    Equal< FL3.Absurd, FL4.Absurd > | false,    false>

Ну да ладно, у нас ещё остались не разобранными строковые перечисления. Может показаться, что ведут они себя как и числовые, однако, внезапно:


enum HappyDebugging {    False = "True",     True = "False",}type True_extends_string = Assert<    Classify< HappyDebugging.True, string >,    [ HappyDebugging.True, '<:', string ]>

Получается, что number является подтипом числового перечисления, а вот string подтипом строкового уже нет.


Призраки прошлого


В Тайпскрипте есть пара специальных типов, которые находятся на противоположных концах иерархии:


  • never представляет из себя пустое множество значений. То есть он является подтипом любого типа, и никакой другой тип не может быть его подтипом.
  • unknown же это множество всех возможных значений. То есть это объединение вообще всех типов в один. Поэтому любой тип является подтипом unknown.

Но что это маячит рядом с ними? Да это же any! С одной стороны он полностью взаимозаменяем с unknown:


type unknown_is_any = Assert<    unknown,    any>

Но с другой же, он как кот Шрёдингера является подтипом never (и как следствие, любого другого типа до unknown) и не является таковым одновременно:


type any_maybe_extends_never = Assert<    any extends never ? true : false,    true | false>

Короче, any пробивает дно во всех возможных смыслах. Тяжела участь тех, кто столкнётся с ним лицом к лицу...



Весь код из статьи.


Счастливой отладки, ребята!

Источник: habr.com
К списку статей
Опубликовано: 03.12.2020 18:17:56
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Разработка веб-сайтов

Программирование

Typescript

Type systems

Types

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru