node.js - TypeScript kan ikke kompilere Mongoose spørringshjelper
Jeg jobber med et TypeScript-prosjekt som bruker Mongoose og blir kompilert med tsc. Når jeg prøver å bruke en spørringshjelper med .find():
import { InferRawDocType, Schema, model } from "mongoose";
export const mySchemaDefinition = {
key: "string",
value: "string"
} as const;
export type RawSchema = InferRawDocType<typeof mySchemaDefinition>;
export const mySchema = new Schema(mySchemaDefinition, {
query: {
byKey: function (key: string) {
return this.where({ key: key });
}
}
});
export const MyModel = model("MyModel", mySchema);
MyModel.find({ key: "123" }).exec(); // OK
MyModel.findOne().byKey("123").exec(); // OK
MyModel.find().byKey("123").exec(); // Error
TypeScript kaster en feil under transpillering:
The 'this' context of type 'QueryWithHelpers<(Document<unknown, { byKey: <T extends Query<unknown, Document<unknown, {}, FlatRecord<{ readonly key: "string"; readonly value: "string"; }>> & FlatRecord<{ readonly key: "string"; readonly value: "string"; }> & { _id: ObjectId; } & { __v: number; }, {}, FlatRecord<{ readonly key: "string"; readonly value: "string"; }>, "find", Record<string, never>>>(this: T, key: string) => T; }, { readonly key: "string"; readonly value: "string"; }> & { readonly key: "string"; readonly value: "string";
} & { _id: ObjectId; } & { __v: number; })[], Document<unknown, { byKey: <T extends Query<unknown, Document<unknown, {}, FlatRecord<{ readonly key: "string"; readonly value: "string"; }>> & FlatRecord<{ readonly key: "string"; readonly value: "string"; }> & { _id: ObjectId; } & { __v: number; }, {}, FlatRecord<{ readonly key: "string"; readonly value: "string"; }>, "find", Record<string, never>>>(this: T, key: string) => T; }, { readonly key: "string"; readonly value: "string"; }> & { readonly key: "string"; readonly value: "string"; } & { _id: ObjectId; } & { __v: number; }, { byKey: <T extends Query<unknown, Document<unknown, {}, FlatRecord<{ readonly key: "string"; readonly value: "string"; }>> & FlatRecord<{ readonly key: "string"; readonly value: "string"; }> & { _id: ObjectId; } & { __v: number; }, {}, FlatRecord<{ readonly key: "string"; readonly value: "string"; }>, "find", Record<string, never>>>(this: T, key: string) => T; }, { readonly key: "string"; readonly value: "string"; }, "find", {}>' is not assignable to method's 'this' of type 'Query<unknown, Document<unknown, {}, FlatRecord<{ readonly key: "string"; readonly value: "string";
}>> & FlatRecord<{ readonly key: "string"; readonly value: "string"; }> & { _id: ObjectId; } & { __v: number; }, {}, FlatRecord<{ readonly key: "string"; readonly value: "string"; }>, "find", Record<string, never>>'.
The types returned by 'lean(...).exec()' are incompatible between these types.
Type 'Promise<({ readonly key: "string"; readonly value: "string"; } & { _id: ObjectId; } & { __v: number; })[]>' is not assignable to type 'Promise<{ readonly key: "string"; readonly value: "string"; } & { _id: ObjectId; } & { __v: number; }>'.
Type '({ readonly key: "string"; readonly value: "string"; } & { _id: ObjectId; } & { __v: number; })[]' is not assignable to type '{ readonly key: "string"; readonly value: "string"; } &
{ _id: ObjectId; } & { __v: number; }'.
Type '({ readonly key: "string"; readonly value: "string"; } & { _id: ObjectId; } & { __v: number; })[]' is missing the following properties from type '{ readonly key: "string"; readonly value: "string"; }': key, value
Men det skjer bare med .find, .findOne fungerer fint.
this-konteksten ser ut til å være riktig type.
Er dette et problem med tsc-konfigurasjonen, eller er det en Mongoose-typefeil jeg kan fikse?
Byggekommando:
tsc index.ts --esModuleInterop --outDir ./build --noErrorTruncation
tsconfig.json (består hovedsakelig for React-appen som er i samme prosjekt):
{
"compilerOptions": {
"target": "es5",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx"
},
"include": ["src"]
}
Svar #1
Feilen oppstår fordi byKey-spørringshjelperen ikke er riktig typet for å håndtere både find() og findOne()-operasjoner. Den nåværende implementeringen antar en enkelt dokumentreturtype, men find() returnerer et array. Jeg håper det vil fikse det.
export const mySchemaDefinition = {
key: "string",
value: "string"
} as const;
export const mySchema = new Schema(mySchemaDefinition, {
query: {
byKey: function<T extends Query<any, any>>(this: T, key: string) {
return this.where({ key: key });
}
}
});
Denne endringen gjør byKey-spørringshjelperen generell nok til å fungere med både find() og findOne()-operasjoner.
For å unngå å bruke any i Typescript-kode, kan du definere en spesifikk type for skjemaet ditt og bruke det i Query-typen. Slik kan du gjøre det:
type MySchemaType = {
key: string;
value: string;
};
export const mySchemaDefinition = {
key: "string",
value: "string"
} as const;
export const mySchema = new Schema(mySchemaDefinition, {
query: {
byKey: function<T extends Query<MySchemaType, typeof
mySchemaDefinition>>(this: T, key: string) {
return this.where({ key: key });
}
}
});
Svar #2
Det er kanskje ikke den mest praktiske eller optimale løsningen, men det illustrerer poenget.
import { InferRawDocType, Schema, model, Query } from "mongoose";
export const mySchemaDefinition = {
key: "string",
value: "string"
} as const;
export type RawSchema = InferRawDocType<typeof mySchemaDefinition>;
export const mySchema = new Schema<RawSchema>(mySchemaDefinition, {
query: {
byKey: function<T extends Query<any, RawSchema>>(this: T, key: string) {
return this.where({ key: key });
}
}
});
export const MyModel = model("MyModel", mySchema);
MyModel.find({ key: "123" }).exec(); // OK
MyModel.findOne().byKey("123").exec(); // OK
MyModel.find().byKey("123").exec(); // OK
eller hvis du vil unngå å bruke Query<any, RawSchema>
import { Schema, model, Document, Model } from "mongoose";
interface MyDocument extends Document {
key: string;
value: string;
}
const mySchema = new Schema<MyDocument>({
key: { type: String, required: true },
value: { type: String, required: true }
});
mySchema.query.byKey = function (key: string) {
return this.where({ key });
};
const MyModel: Model<MyDocument> = model("MyModel", mySchema);
MyModel.find({ key: "123" }).exec(); // OK
MyModel.findOne().byKey("123").exec(); // OK
MyModel.find().byKey("123").exec(); // OK
eller til og med
import { InferRawDocType, Schema, model, Query } from "mongoose";
export const mySchemaDefinition = {
key: "string",
value: "string"
} as const;
export type RawSchema = InferRawDocType<typeof mySchemaDefinition>;
export const mySchema = new Schema<RawSchema>(mySchemaDefinition, {
query: {
byKey: function(this: Query<RawSchema[], RawSchema>, key: string) {
return this.where({ key: key });
}
}
});
export const MyModel = model("MyModel", mySchema);
MyModel.find({ key: "123" }).exec(); // OK
MyModel.findOne().byKey("123").exec(); // OK
MyModel.find().byKey("123").exec(); // OK
