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

Добро пожаловать в серию Mastering TypeScript, в которой десятки статей. Чтобы помочь читателям лучше закрепить свои знания о TypeScript, я выбрал несколько десятков заданий из репозитория type-challenges на Github, чтобы выполнить задание вместе с вами.
- Требования к типу: реализация встроенного типа утилиты Pick‹T, K›
- Проблемы с типом: реализация встроенного служебного типа Omit‹T, K›
- Требования к типу: реализация типа утилиты RequiredByKeys‹T, K›
Испытание
Реализуйте общий 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 разработчиков создавать приложения с компонентами.
Превратите любой пользовательский интерфейс, функцию или страницу в компонент многократного использования — и поделитесь им со своими приложениями. Легче сотрудничать и строить быстрее.
Разделите приложения на компоненты, чтобы упростить разработку приложений, и наслаждайтесь наилучшими возможностями для рабочих процессов, которые вы хотите: