Может быть, вам нужны такие типы:
type PartialK<T, K extends PropertyKey = PropertyKey> =
Partial<Pick<T, Extract<keyof T, K>>> & Omit<T, K> extends infer O ?
{ [P in keyof O]: O[P] } : never;
Здесь PartialK<T, K>
действует как Partial<T>
, но только для ключей в K
, оставляя остальные в покое. Так что PartialK<T, keyof T>
или PartialK<T, PropertyKey>
должны действовать как Partial<T>
. Это должно работать достаточно хорошо, если T
является типом объекта без сигнатур индекса, сигнатур вызовов или конструктивных сигнатур. Я проделал небольшой трюк с условными типами, чтобы у результирующего типа объекта не было пересечения, например:
type TestPartialK = PartialK<{ a: string, b: number, c: boolean }, "b" | "c">
/* type TestPartialK = {
b?: number | undefined;
c?: boolean | undefined;
a: string;
} */
Теперь это работает только на один уровень глубиной, и это похоже на ваш вопрос хочет сделать это рекурсивно через вложенные объекты. Так вот NestedPartialK<T, K>
:
type NestedPartialK<T, K extends PropertyKey = PropertyKey> =
T extends Function ? T :
T extends Array<any> ? Array<NestedPartialK<T[number], K>> :
T extends object ? PartialK<{ [P in keyof T]: NestedPartialK<T[P], K> }, K> :
T;
Он использует PartialK
в своем определении; он оставляет функции и примитивы в покое и использует рекурсивные типы для отображения массивов в массивы, а объектов в объекты. Обратите внимание, что он не повторяет кортежи, но частичные кортежи в любом случае странны, и в вашем сценарии использования даже не упоминаются массивы, поэтому меня это не сильно беспокоит. Давайте посмотрим, работает ли он на вашем FooDatabase
:
type Foo = NestedPartialK<FooDatabase, "_id">
/* type Foo = {
_id?: string | undefined;
foo: string;
bar: string;
tree: {
_id?: string | undefined;
again: {
_id?: string | undefined;
value: string;
};
};
} */
Выглядит хорошо для меня. Все реквизиты _id
теперь являются необязательными, а остальные реквизиты оставлены в покое. Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код