Мой сценарий: я пытаюсь создать типобезопасную оболочку вокруг драйвера 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;
}
Спасибо за ваше время 🙏
Я думаю, вам нужно 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
было эффективно подавлено.
Детская площадка, ссылка на код