Встроенные частичные дженерики не являются гибкими, на помощь приходят дженерики PartialByKeys.

Добро пожаловать в серию Mastering TypeScript, в которой десятки статей. Чтобы помочь читателям лучше закрепить свои знания о TypeScript, я выбрал несколько десятков заданий из репозитория type-challenges на Github, чтобы выполнить задание вместе с вами.



Испытание

Реализуйте общий PartialByKeys<T, K>, который принимает два аргумента типа T и K.

K укажите набор свойств T, которые должны быть необязательными. Когда K не указан, он должен сделать все свойства необязательными, как и обычный Partial<T>.

Например:

interface User {
  name: string
  age: number
  address: string
}
type UserPartialName = PartialByKeys<User, 'name'> 
// { name?:string; age:number; address:string }

Решение

Наша задача типа состоит в том, чтобы реализовать универсальный PartialByKeys<T, K>. Когда K не предоставляется, он должен сделать все свойства необязательными, как и обычный Partial<T>. Итак, давайте сначала разберемся, что делает Partial<T> generic.

Partial<T>

Создает тип со всеми свойствами Type, установленными как необязательные.

Partial<T> — это тип встроенной утилиты TypeScript, который определен в файле typescript/lib/lib.es5.d.ts:

/**
 * Make all properties in T optional. 
 * typescript/lib/lib.es5.d.ts
 */
type Partial<T> = {
    [P in keyof T]?: T[P];
};

Универсальный Partial<T> использует сопоставленный тип TypeScript внутри, и его синтаксис выглядит следующим образом:

Где P in K похож на оператор JavaScript for...in, который используется для перебора всех типов в типе K, и переменная типа T, которая используется для представления любого типа в TypeScript.

Вы также можете использовать дополнительные модификаторы только для чтения и вопросительный знак (?) в процессе сопоставления. Соответствующие модификаторы добавляются и удаляются путем добавления префиксов плюс (+) и минус (-). По умолчанию используется знак "плюс", если префикс не добавлен.

После введения соответствующих знаний о сопоставленных типах идея реализации универсального PartialByKeys<T, K> очень проста.

Как видно из приведенного выше рисунка, нам нужно только выбрать свойства, связанные с K, установить их как необязательные и сгенерировать новый тип объекта, затем создать другой тип объекта на основе оставшихся свойств и наконец, используйте оператор & для преобразования двух вышеуказанных типов объектов в новый тип объектов.

Если вы хотите узнать больше о типах перекрестков, прочитайте эту статью.



Полный код

Наконец, давайте посмотрим на полный код:



TypeScript 4.1 позволяет нам переназначать ключи в сопоставленных типах с помощью предложения as. Его синтаксис следующий:

type MappedTypeWithNewKeys<T> = {
    [K in keyof T as NewKeyType]: T[K]
    //            ^^^^^^^^^^^^^
    //            New Syntax!
}

где тип NewKeyType должен быть подтипом типа объединения string | number | symbol. В процессе переназначения ключей мы можем фильтровать ключи, возвращая тип never.

Роль универсального Merge заключается в объединении типов объектов. Помимо вышеперечисленных решений, есть более лаконичный способ.

type PartialByKeys<T, K = keyof T> = Merge<
  {
    [P in keyof T as P extends K ? P : never]?: T[P]
  } & Pick<T, Exclude<keyof T, K>
>

В приведенном выше коде мы используем встроенный тип утилиты TypeScript Pick, если вы хотите узнать больше об этом универсальном типе, вы можете прочитать следующую статью:



Основное знание, связанное с этой задачей, — это сопоставленные типы в TypeScript. Если вы хотите узнать больше о сопоставленных типах, прочитайте следующую статью:



Если у вас есть другие решения, вы можете дать мне сообщение. Вы также можете подписаться на меня в Medium или Twitter, чтобы узнать больше о TS и JS!

Ресурсы





Создавайте приложения с повторно используемыми компонентами, такими как Lego.

Инструмент с открытым исходным кодом Bit помогает более чем 250 000 разработчиков создавать приложения с компонентами.

Превратите любой пользовательский интерфейс, функцию или страницу в компонент многократного использования — и поделитесь им со своими приложениями. Легче сотрудничать и строить быстрее.

Подробнее

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

Микро-интерфейсы

Система дизайна

Совместное использование кода и повторное использование

Монорепо

Узнать больше