Частичное <> по заданному ключу c - PullRequest
0 голосов
/ 21 января 2020

У меня есть интерфейс машинописного текста, описывающий данные базы данных mongodb, например:

interface Foo {
  _id: string,
  name: string,

  fish: {
    _id: string,
    name: string,   
  },
}

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

function pushNewFish() {
   const obj: Foo = {
      name: 'obiwan',

      fish: {
         name: 'kenobi',
      },
  };

  // ...
}

Проблема в : я не реализую все _id элементы, которые указывают c на базу данных.

У меня есть возможность использовать Partial<Foo>, но это не то, что я ищу for.

У меня также есть возможность превратить каждый _id: string в _id ?: string, но это неправильно.

Есть ли способ сделать:

interface FooDatabase { ... }

type Foo = Partial<'_id', FooDatabase>;

? Спасибо

Ответы [ 2 ]

1 голос
/ 21 января 2020

Может быть, вам нужны такие типы:

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 теперь являются необязательными, а остальные реквизиты оставлены в покое. Хорошо, надеюсь, это поможет; удачи!

Детская площадка ссылка на код

1 голос
/ 21 января 2020

Если только некоторые _id поля должны быть необязательными, тогда определите новый тип утилиты, который будет объявлять только выбранные ключи как необязательные:

type Optional<T, K extends keyof T> = Partial<Pick<T, K>> & Omit<T, K>

interface FooDatabase {
  _id: number;
  foo: string;
  bar: string;
}

type Foo = Optional<FooDatabase, '_id'>

Playground Link


Рекурсивное решение (для вложенных объектов), где каждая клавиша одного и того же типа синглтона сделана необязательной:

type Primitive = number | string | boolean | undefined | null | any[];
type Optional<T, K extends keyof T> = { [P in K]?: T[P] } & { [P in Exclude<keyof T, K>]: T[P] extends Primitive ? T[P] : Optional<T[P], keyof T[P] & K> };

Playground Link

...