Репликация агрегации проекта 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;
}

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


1
87
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 было эффективно подавлено.

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