068 Keyof
keyof
говорит нам, что мы берём от определённого объекта только ключи.
Конкретно тут в примере, в константу key
мы можем положить только ключи интерфейса IUser
И вот пример, в котором мы хотим получить в константе userName
имя пользователя, но встречаемся с такой проблемой, что можем написать в качестве аргумента любое значение и не увидим ошибки от компилятора
И теперь, используя дженерики, мы можем обозначить, что наша функция должна принимать в себя первое значение - типа T
, и второе - типа K
. Второе значение (K
) будет расширяться ключами, содержащимися в типе T
.
Конкретно в эту функцию мы первым аргументом передаём объект (ключи: name, age) и вторым аргументом мы должны передать один из ключей объекта (name или age). И функция нам по-итогу вернёт значение ключа нужного нам объекта (тут - user[name] -> return 'John'
)
В данном случае, компилятор нам будет явно указывать, что при вызове метода getValue()
, второе значение обязательно должно быть одним из ключей первого аргумента
069 Упражнение - Пишем функцию группировки
Необходимо написать функцию группировки, которая принимает массив объектов и его ключ, производит группировку по указанному ключу и возвращает сгруппированный объект.
Пример:
При группироке по group
:
070 Typeof
Тут сразу нужно сказать, что компилятор ТС, когда мы пишем юнион тип данных покажет нам, что тип юнион. Однако уже внутри других структур (например, условий) мы явно увидим тип переменной (от значения внутри)
Стоковый typeof
из JS выполняет для нас операцию проверки типа данных в переменной. Однако есть и typeof
, который используется в типах и он уже имеет свою реализацию только в ТС
Типовый typeof
внутри ТС позволяет задать тип одной переменной отталкиваясь от другой переменной
И обычно используется typeof
в паре с keyof
. В примерах ниже мы получаем типы наших объектов. Конкретно мы можем достать в качестве типа ключ объекта. Обычный keyof
достанет только само наименование ключа из объекта, а не тип.
071 Indexed Access Types
Мы имеем в основе своей два компонента ТС: то, что попадает непосредственно в рантайм после компиляции
И то, что не попадает в рантайм (существует только внутри ТС). Тут хочется упомянуть, что второй тайп работать не будет, так как тут он принимает в себя не тип, а значение от рантайм-части языка
Конкретно в этом примере roleType2
работать будет, так как мы обращаемся к внутреннему средству языка - типам и через typeof
получаем тип объекта
Тут уже нужно напомнить, что константы принимают в себя литеральные типы данных (тип константы = содержащемуся в ней значению). Однако тот же трюк, что и выше, но с let
- не пройдёт
Однако с let
получится сделать тайпоф, но нужно будет конкретно задать тип для переменной в виде нашего значения
Однако в ТС опять же есть механизм из JS, который применим только для типов.
В обычных условиях [значения][индекс_значения]
позволяет нам обратиться к значению по индексу во вторых скобках.
Но через type name = Interface[значения][number]
, где значением является массив - в ТС мы говорим, что обращаемся к ключам объекта массив, которые представляют собой числа (0: значение, 1: значение)
Дальше представим, что мы хотим сделать юнион из всех элементов массива внутри константы Первым делом, мы можем кастануть наш массив как константу, тем самым создав юнион тип у переменной И дальше в тайпе через тайпоф определим массив нашей константы и будем просить отдельные элементы в качестве юнионов
Такой подход удобен, когда нам нужно проверять тип пользователя, которого мы получаем (например)
И так же мы можем получать вложенные типы из наших интерфейсов. Наш интерфейс User имеет свойство, которое принимает в себя интерфейс Permission. Сам же Permission представляет из себя объект, который принимает в себя свойство с типом Дата.
Через тайп ['permissions']['endDate']
мы получаем вложенный внутри свойства permission тип свойства endDate, который уже равен Дате
072 Conditional Types
Conditional-условия нам позволяют в одну строку записать какие-то короткие для выполнения условия
Conditional же в типах позволяет нам определить какой тип будет у объекта при определённых вводных. Конкретно в примере, data
будет иметь тип строки, если в дженерик передали success
И теперь, в зависимости от типа передаваемого дженерика, мы можем определить, какие типы мы можем поместить внутрь производного от интерфейса объекта
Так же кондишенелы могут нам пригодиться для упрощения перегрузок больших функций. Конкретно тут в примере, мы проверяем функцию на то, какое значение мы в неё вложили - число или строку
Тут уже приведён пример использования кондишенл типов для проверки возвращаемого типа из тайпа. Данный тайп содержит в себе generic
, который при расширении от number
будет возвращать User
, а в остальных случаях - UserPersistend
Однако нам нужно обязательно будет скастовать return
к нашему возвращаемому типу, так как type
не работают в рантайме и выдают ошибку
073 Infer
Представим, что у нас есть определённая транзакция, которая происходит от одного значения к другому
Чтобы всё заработало, нужно явно типизировать нашу транзакцию так:
Либо так:
infer
- это ключевое слово, которое выведет нам определённый тип из пока ещё несущестующего.
Тут infer
занимается псевдообъявлением переменной типа First
внутри нашего тайпа и позволяет её вытащить в наш возвращаемый тип (использовать в кондишенеле возвращения)
И чисто фактически мы вытащили тип в возвращаемое значение. В саму переменную транзакции мы запихнём дженерик от функции нашего тайпа.
infer
обычно используется в каких-то плохо-типизируемых библиотеках для получения определённого аргумента и использования этого аргумента
074 Mapped Types
Мы создали систему, где раздаём пользователям права на редактирование отдельных элементов системы. Где-то пользователь может только читать, где-то изменять или создавать. Нам нужно реализовать проверку того, что у пользователя есть определённое право в принципе.
Мы можем задать проверку наличия доступа у пользователя к определённым элементам системы либо так: пишем каждое свойство из объекта и проверяем его через булеан.
Либо так. Конкретно этот тайп говорит нам о том, что каждое свойство в объекте должно иметь boolean-значения.
И вот пример использования самого простого мапа, который будет проверять все значения нашего объекта, чтобы они были boolean
Так же мы можем регулировать необязательность свойств и добавлять модификаторы
Так же через прокаст можно поменять наименование ключей в объекте
Так же через синтаксис Exclude<>
мы можем убрать из маны ненужные нам значения. Первый аргумент - сами принимаемые значения, второй аргумент - то, что мы должны исключить из выборки
Так же через вилку можно добавить ещё значения для исключения из map
075 Упражнение - Валидация форм
Нам нужно провалидировать форму. Форма в себя обязательно принимает имя и пароль. Мы отдельно создали форму с именем и паролем.
Дальше нам нужно написать тайп, который будет проверять валидна ли форма. Ключ у нас находится в множестве типа Т. Значением у свойства могут быть два объекта (которые записаны через вилку “|
”)
И уже конечный блок кода выполняет саму валидацию полученных значений
076 Template Literal Types
Шаблоны литеральных типов представляют из себя плэйсхолдеры для других типов, которые позволяют нам модифицировать уже имеющиеся типы под другие сущности
Так же мы можем модифицировать типы внутри этих шаблонов (например, перевести полученные типы в заглавные буквы)
Дженерик Capitalize<>
переводит первые буквы в заглавные
Так же добавив ещё один шаблон, мы увеличим количество типов вдвое (через Bulk
добавятся неизменные варианты - пустая строка и варианты с Bulk
)
Самый простой и частый пример использования таких темплейтов - это задание шаблонов значения для разных типов ответов (например, нам нужно реализовать error
в http и для другой структуры)
Или на том же фронте отделить fade-In и fade-Out через такую конструкцию
Так же мы можем распаковать наш составной тип и работать с его изначальным значением.
То есть в этом примере через infer
(в ReadOrWriteBulk
) мы достали значение и и убрали его