Репликация агрегации проекта mongodb в Typescript

Мой сценарий: я пытаюсь создать типобезопасную оболочку вокруг драйвера node-mongodb. Мне трудно определить тип возвращаемого значения для этапа агрегации проекта.

Посмотрите TS Playground здесь

class BaseAggregate<Coll> {
  private pipeline: Pipeline<Coll>[] = [];
  constructor(initialPipeline?: typeof this.pipeline) {
    if (initialPipeline) {
      this.pipeline = initialPipeline;
    }
  }

  match(stageInput: DeepPartial<Coll>) {
    return new BaseAggregate<Coll>([...this.pipeline, { $match: stageInput }]);
  }

  projectActual(stageInput: Record<keyof Coll, 0 | 1>) {
    return new BaseAggregate<T>>([
      //  what goes here ----^ instead of T?
      //   ...this.pipeline,
      //   { $project: stageInput },
    ]);
  }
}


interface Test {
  name: string;
  email: string;
}

const agg = new BaseAggregate<Test>()
  .match({ name: "john" })
  .match({ email: "sir john" })
  .match({ email: "sir" })
  .projectActual({ email: 0, name: 1 })

Мне нужна помощь в объявлении типа возвращаемого значения функции projectActual(), чтобы тип возвращаемого значения для приведенного выше примера был

interface {
name: string;
}

Спасибо за ваше время 🙏

«Я ожидал ошибку в этой строке [k: number]: boolean» Почему? Непонятно, чего вы пытаетесь достичь. StringMap<string | number | boolean>; оценивается как {}, и ваш UserSettings правильно расширяется {}.

Robby Cornelissen 11.07.2024 05:32
Record<StringKeys<T>, T> на самом деле не имеет смысла, поскольку StringKeys, похоже, ожидает объект для использования с keyof
Bergi 11.07.2024 05:41

@Bergi да, настраиваюсь на использование keyof.

Abd 11.07.2024 07:45

@RobbyCornelissen Я пытаюсь собрать построитель запросов для mongodb. Я пытаюсь реализовать этап проекта конвейера агрегации mongodb. Я добавлю дополнительную информацию в вопрос.

Abd 11.07.2024 07:46

В вашей ссылке на игровую площадку содержится значительное количество кода, которого нет в виде открытого текста; пожалуйста, добавьте его сюда или удалите из ссылки... минимальный воспроизводимый пример здесь, в вопросе, должен быть открытым текстом. Это важно? ChangeToZeroOrOne довольно сложно понять, что на самом деле Record<keyof T, 0 | 1> (T[K] extends any всегда верно). И DeepPartial не требует проверки object (что естественно происходит с гомоморфными отображаемыми типами). Я пытаюсь выяснить, важно ли то, что написано в этой ссылке, или это отвлекает. Пожалуйста, отредактируйте, чтобы уточнить.

jcalz 11.07.2024 16:10

(см. предыдущий комментарий) • Я также не понимаю, какое отношение новое имеет к «строковым ключам», а не к цифровым клавишам. Как это связано? • Если я приму ваш код варианта использования открытого текста за чистую монету, я бы сказал, что вы можете использовать сопоставление типов , как показано в этой ссылке на игровую площадку . Соответствует ли это вашим потребностям? Если да, то я напишу ответ с объяснением; если нет, то что мне не хватает?

jcalz 11.07.2024 16:14

@jcalz Я убрал детскую площадку. Предоставленная вами ссылка на игровую площадку — это то, что я ищу. Я попробовал реализовать ваше решение, и ts больше не жалуется, но я потерял вывод типа, когда связывал методы. Пожалуйста, осмотрите детскую площадку. 🙏

Abd 11.07.2024 19:11

@jcalz, я изменил вопрос здесь. Пример, который вы предоставляете, - это то, что я ищу, но когда я применил его к функции ProjectActual, TS больше не жалуется, но вывод типа пропал, попробуйте связать еще один метод с нижней строкой. ``` const agg = new BaseAggregate<Test>() .match({ name: "john" }) .match({ электронная почта: "сэр Джон" }) .match({ электронная почта: "сэр" }) .projectActual( { email: 0, name: 1 }) ``` Раньше я использовал машинописный текст, но никогда на этом уровне, и я все еще пытаюсь осознать это. Пожалуйста, дайте мне знать, если вы знаете какой-либо лучший подход к этому.

Abd 11.07.2024 20:06

Соответствует ли эта версия вашим потребностям? Если да, то я напишу ответ с объяснением. В любом случае, важно: пожалуйста отредактируйте, чтобы убрать мой предложенный код без указания авторства из вашего вопроса (это часть ответа, а не вашего вопроса), и вместо этого вставьте пример вашего кода с методами и т. д. и объясните, что ваши варианты использования. Как открытый текстовый код, так и словесные описания в тексте самого вопроса. (Не только как комментарий. Комментарии могут быть удалены без следа.)

jcalz 11.07.2024 21:09

Да, @jcalz, вот и все 😃. ТИСМ

Abd 12.07.2024 04:43

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

jcalz 12.07.2024 12:08

@jcalz, пожалуйста, отредактируйте по своему желанию.

Abd 12.07.2024 14:21

Я сделаю это, когда у меня будет возможность, возможно, завтра (я путешествую сегодня)

jcalz 12.07.2024 15:43
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
1
14
87
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я думаю, вам нужно projectActual() иметь следующую общую позывную:

declare projectActual<R extends Record<keyof T, 0 | 1>>(
  stageInput: R
): BaseAggregate<{ [K in keyof T as R[K] extends 1 ? K : never]: T[K]; }>

Таким образом, stageInput имеет общий тип R , ограниченный до Record<keyof T, 0 | 1>, что означает, что stageInput должен иметь каждый ключ, который есть T, но значения должны быть либо 0, либо 1.

Тогда аргументом типа для выходного типа BaseAggregate является { [K in keyof T as R[K] extends 1 ? K : never]: T[K] }, отображаемый тип 🔁 с переназначением ключа , который для каждого ключа K проверяет, является ли R[K]1. Если да, то свойство выводится как есть, в противном случае оно подавляется (если вы переназначаете ключ на never, он исключается из результирующего типа). По сути, это Pick<T, Keys>, где Keys — это набор ключей R, имеющих тип значения 1.


Давайте проверим это:

 interface Test {
  name: string;
  email: string;
}

const agg = new BaseAggregate<Test>()
  .match({ name: "john" })
  .match({ email: "sir john" })
  .match({ email: "sir" })
  .projectActual({ email: 0, name: 1 })

/* const agg: BaseAggregate<{
    name: string;
}> */

Выглядит неплохо. agg имеет тип BaseAggregate<{name: string}>; свойство email было эффективно подавлено.

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

Другие вопросы по теме