Мой сценарий: я пытаюсь создать типобезопасную оболочку вокруг драйвера 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;
}
Спасибо за ваше время 🙏
Record<StringKeys<T>, T>
на самом деле не имеет смысла, поскольку StringKeys
, похоже, ожидает объект для использования с keyof
@Bergi да, настраиваюсь на использование keyof.
@RobbyCornelissen Я пытаюсь собрать построитель запросов для mongodb. Я пытаюсь реализовать этап проекта конвейера агрегации mongodb. Я добавлю дополнительную информацию в вопрос.
В вашей ссылке на игровую площадку содержится значительное количество кода, которого нет в виде открытого текста; пожалуйста, добавьте его сюда или удалите из ссылки... минимальный воспроизводимый пример здесь, в вопросе, должен быть открытым текстом. Это важно? ChangeToZeroOrOne
довольно сложно понять, что на самом деле Record<keyof T, 0 | 1>
(T[K] extends any
всегда верно). И DeepPartial
не требует проверки object
(что естественно происходит с гомоморфными отображаемыми типами). Я пытаюсь выяснить, важно ли то, что написано в этой ссылке, или это отвлекает. Пожалуйста, отредактируйте, чтобы уточнить.
(см. предыдущий комментарий) • Я также не понимаю, какое отношение новое имеет к «строковым ключам», а не к цифровым клавишам. Как это связано? • Если я приму ваш код варианта использования открытого текста за чистую монету, я бы сказал, что вы можете использовать сопоставление типов , как показано в этой ссылке на игровую площадку . Соответствует ли это вашим потребностям? Если да, то я напишу ответ с объяснением; если нет, то что мне не хватает?
@jcalz Я убрал детскую площадку. Предоставленная вами ссылка на игровую площадку — это то, что я ищу. Я попробовал реализовать ваше решение, и ts больше не жалуется, но я потерял вывод типа, когда связывал методы. Пожалуйста, осмотрите детскую площадку. 🙏
Примечание. Было бы очень полезно, если бы вы отредактировали , чтобы перенести сюда соответствующий код с игровой площадки в виде открытого текста в вопросе. Я рад взглянуть на игровую площадку, но она также должна быть здесь, в Stack Overflow. Я не вижу здесь ничего о цепочке методов (да и о методах вообще). Такое ощущение, что вы задаете дополнительный вопрос о какой-то проблеме, с которой столкнулись после решения этой, но я не могу быть уверен без минимального воспроизводимого примера в самом вопросе. И когда я смотрю на игровую площадку, я не понимаю, в чем заключается проблема вывода.
@jcalz, я изменил вопрос здесь. Пример, который вы предоставляете, - это то, что я ищу, но когда я применил его к функции ProjectActual, TS больше не жалуется, но вывод типа пропал, попробуйте связать еще один метод с нижней строкой. ``` const agg = new BaseAggregate<Test>() .match({ name: "john" }) .match({ электронная почта: "сэр Джон" }) .match({ электронная почта: "сэр" }) .projectActual( { email: 0, name: 1 }) ``` Раньше я использовал машинописный текст, но никогда на этом уровне, и я все еще пытаюсь осознать это. Пожалуйста, дайте мне знать, если вы знаете какой-либо лучший подход к этому.
Соответствует ли эта версия вашим потребностям? Если да, то я напишу ответ с объяснением. В любом случае, важно: пожалуйста отредактируйте, чтобы убрать мой предложенный код без указания авторства из вашего вопроса (это часть ответа, а не вашего вопроса), и вместо этого вставьте пример вашего кода с методами и т. д. и объясните, что ваши варианты использования. Как открытый текстовый код, так и словесные описания в тексте самого вопроса. (Не только как комментарий. Комментарии могут быть удалены без следа.)
Да, @jcalz, вот и все 😃. ТИСМ
Пожалуйста, не вставляйте мои предложения в свой вопрос. Я напишу ответ, когда у меня будет возможность. Решение содержится в ответе, а не в вопросе. Я, вероятно, отредактирую ваш вопрос, если вы этого не сделаете, но я думаю, что мы оба предпочли бы, чтобы вы это сделали.
@jcalz, пожалуйста, отредактируйте по своему желанию.
Я сделаю это, когда у меня будет возможность, возможно, завтра (я путешествую сегодня)
Я думаю, вам нужно 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
было эффективно подавлено.
«Я ожидал ошибку в этой строке
[k: number]: boolean
» Почему? Непонятно, чего вы пытаетесь достичь.StringMap<string | number | boolean>;
оценивается как{}
, и вашUserSettings
правильно расширяется{}
.