DevGang
Авторизоваться

Реализация Pick в TypeScript

Задача этого типа состоит в том, чтобы создать свою собственную версию pick (разумеется, без медиатора), не используя какие-либо служебные типы. Также предоставим вам информацию о дополнительных типах утилит для манипуляции данными и типами.

Ссылка: https://github.com/type-challenges/type-challenges/blob/main/questions/00004-easy-pick/README.md

Теги:

  • Индексированные типы доступа/типы поиска
  • Сопоставленные типы
  • Общие ограничения

Объяснение:

Нам нужно создать новый тип объекта только со свойствами как в Type, так и в Union

type MyPick<Type, Union> = /* ... */

Типы поиска

Чтобы получить тип ключей (ключи также известны как индексированные типы доступа или типы поиска) в Chair, мы можем использовать обозначения в квадратных скобках точно так же, как доступ к свойству или методу в объекте JavaScript.

type Chair = {
    color: string,
    legCount: number,
    isComfortable: boolean
}

Chair["color"] // string
Chair["legCount"] // number

Обратите внимание, что для индексации можно использовать только типы

const key = "color"
Chair[key] // Type "key" cannot be used as an index type

Сопоставленные типы

Как мы можем выполнить итерацию по объединению и создать новый тип объекта? Ответ заключается в сопоставлении типов.

Например, мы можем установить для каждого ключа Chair значение boolean, подобное этому, с помощью динамических ключей и оператора in.

type ChairKeysUnion = "color" | "legCount" | "isComfortable"

type BooleanChair = {
    [Key in ChairKeysUnion]: boolean
} 
/*
type BooleanChair = {
    color: boolean;
    legCount: boolean;
    isComfortable: boolean;
}
*/

Обратите внимание, что Key в этом примере является параметром, поэтому это просто имя-заполнитель. Это можно было бы назвать Property или Jimothy и работать с таким же успехом.

Общие ограничения

Но почему мы не можем просто объединить сопоставленные типы и типы поиска, чтобы присвоить каждому свойству в объединении соответствующий тип поиска, как это?

type MyPick<Type, Union> = {
    [Key in Union]: Type[Key]
}

На данный момент MyPick не является typesafe — он принимает любые свойства в Union. В случае, если свойство не существует в Type, переданном в MyPick, оно будет в новом типе объекта со значением unknown.

type NewChair = MyPick<Chair, 'color' | 'isComfortable' | 'invalid'>
/*
type NewChair = {
    color: string;
    isComfortable: boolean;
    invalid: unknown;
}*/

keyof

Чтобы ограничить Union только типами поиска в Type, нам сначала понадобятся типы поиска Type. Для этого мы можем использовать оператор keyof. Он принимает тип объекта и возвращает строку, объединение строк или объединение чисел ключей типа.

type Chair = {
    color: string,
    legCount: number,
    isComfortable: boolean
}

type ChairIndexUnion = keyof Chair // "color" | "legCount" | "isComfortable"

Теперь, когда у нас есть оба объединения, мы можем ограничить параметр Union для поиска типов в Type с помощью ключевого слова extends.

type MyPick<Type, Union extends keyof Type> = {
    [Key in Union]: Type[Key]
}

type NewChair = MyPick<Chair, 'color' | 'isComfortable' | 'invalid'>
/*
Type '"color" | "isComfortable" | "invalid"' does not satisfy the constraint 'keyof Chair'.
Type '"invalid"' is not assignable to type 'keyof Chair'.
*/

invalid не находится в Chair, поэтому тип NewChair недопустим.

Теперь у вас есть типобезопасная функция Pick.

#TypeScript
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу