No overload matches this call error with typescript, nodejs and mongoose - node.js

I have a service in an application that was previously working but all of a sudden I am getting this typescript error and I am unsure how to go about solving it. Please see my model file:
import mongoose from "mongoose";
import { AccessType, TicketType } from "./types";
interface TicketAttrs {
name: string;
type: TicketType;
access: AccessType;
uniqueId: string;
price: number;
quantity: number;
creator: string;
}
interface TicketDoc extends mongoose.Document {
name: string;
type: TicketType;
access: AccessType;
uniqueId: string;
price: number;
quantity: number;
creator: string;
}
interface TicketModel extends mongoose.Model<TicketDoc> {
build(attrs: TicketAttrs): TicketDoc;
}
const ticketSchema = new mongoose.Schema(
{
name: {
type: String,
},
type: {
type: String,
enum: Object.values(TicketType),
},
access: {
type: String,
enum: Object.values(AccessType),
},
price: {
type: Number,
},
uniqueId: {
type: String,
},
quantity: {
type: Number,
},
creator: {
type: String,
},
},
{
timestamps: true,
toJSON: {
transform(doc, ret) {
ret.id = ret._id;
delete ret._id;
},
},
}
);
ticketSchema.statics.build = (attrs: TicketAttrs) => {
return new Ticket(attrs);
};
const Ticket = mongoose.model<TicketDoc, TicketModel>("Ticket", ticketSchema);
export { Ticket };
I get an error on ticketSchema on this line const Ticket = mongoose.model<TicketDoc, TicketModel>("Ticket", ticketSchema);
The Error Message is:-
const ticketSchema: mongoose.Schema<mongoose.Document<any>, mongoose.Model<mongoose.Document<any>>>
No overload matches this call.
Overload 1 of 6, '(name: string, schema?: Schema<TicketDoc, TicketModel> | undefined, collection?: string | undefined, skipInit?: boolean | undefined): TicketModel', gave the following error.
Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of type 'Schema<TicketDoc, TicketModel>'.
Types of property 'methods' are incompatible.
Type '{ _id?: any; __v?: number | undefined; $ignore: (path: string) => void; $isDefault: (path: string) => boolean; $isDeleted: (val?: boolean | undefined) => boolean; $isEmpty: (path: string) => boolean; ... 47 more ...; validateSync: (pathsToValidate?: string[] | undefined, options?: any) => NativeError | null; } & { ....' is not assignable to type '{ name: string; type: TicketType; access: AccessType; uniqueId: string; price: number; quantity: number; creator: string; _id?: any; __v?: number | undefined; $ignore: (path: string) => void; ... 50 more ...; validateSync: (pathsToValidate?: string[] | undefined, options?: any) => NativeError | null; } & { ...; }'.
Type '{ _id?: any; __v?: number | undefined; $ignore: (path: string) => void; $isDefault: (path: string) => boolean; $isDeleted: (val?: boolean | undefined) => boolean; $isEmpty: (path: string) => boolean; ... 47 more ...; validateSync: (pathsToValidate?: string[] |
I am also getting an error saying that there is no build function, I don't understand what broke the code and it was working without any changes.

Related

Any better way for me to select between three models based on some variable?

I am currently working on an Express.js API that handles getting and saving various content types, storing one record per piece of content. My issue that I encountered a while ago was how do I select which Mongoose model to use for queries (mainly find and save) depending on the content type specified to the route handler. I want to do this the right way (or rather, I want to use best practices).
Long story short I looked for help and I used an object instead of a map, which led to more issues due to the following object having the following type:
const contentTypeModels = {
"image": imageModel,
"video": videoModel,
"letter": letterModel
};
Type info of contentTypeModels as declared and initialized
const contentTypeModels: {
image: Model<IImage, {}, {}, {}, any>;
video: Model<IVideo, {}, {}, {}, any>;
letter: Model<ILetter, {}, {}, {}, any>;
}
The issue here was that the type expected in the route handler was
Model<IImage | IVideo | ILetter>
Figured this out sometime yesterday (don’t remember exactly how tbh)
I was able to get a working solution with further assistance but I want to know if there’s a better way to dynamically select a model to perform queries on, or how to resolve the type issues I’m currently having if there is no better way than to use an object.
Here’s the interfaces and models for each content type, as well as one route handler and some accompanying code from the top of the file (imports excluded)
const contentTypeModels = {
'image': imageModel,
'video': videoModel,
'letter': letterModel
};
type contentNames = 'image' | 'video' | 'letter' | null;
// other code
// …
const getOneFromDb = (req: Request, res: Response, next: NextFunction) => {
// get object from Db
/*
make request at the URL <contentType>/<mongoId>
*/
const mongoId = req.params.mongoId;
// was getting an error before with this cause i didn’t limit the values of strings that could be accepted by the rest of the code
/*
Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{ image: Model<IImage, {}, {}, {}, any>; video: Model<IVideo, {}, {}, {}, any>; letter: Model<ILetter, {}, {}, {}, any>; }'.
*/
const content = req.params.contentType;
// uncommenting the line below yields the error ‘string’ is not assignable to contentNames
// const content:contentNames = req.params.contentType;
// resolved that error with the following
/*
let content:contentNames;
if (req.params.contentType === 'image' || req.params.contentType === 'video' || req.params.contentType === 'letter') {
content = req.params.contentType;
} else {
content = null;
throw 'bad request: please specify a content type';
}
*/
const contentType = contentTypeModels[content];
if (mongoId.length > 0) {
// the error i was getting before was on the findById() function, it was something like:
/* This expression is not callable.
Each member of the union type '{ <ResultDoc = Document<unknown, any, ILetter> & ILetter & { _id: ObjectId; }>(id: any, projection?: ProjectionType<ILetter> | null | undefined, options?: QueryOptions<...> | ... 1 more ... | undefined, callback?: Callback<...> | undefined): Query<...>; <ResultDoc = Document<...> & ... 1 more ... & { ...; }>(id: any...' has signatures, but none of those signatures are compatible with each other.
*/
contentType.findById(mongoId)
.then(fileFound => {
if (fileFound) {
res.status(200).send(fileFound);
} else {
next(createHttpError(404, 'file not found in db'));
}
})
.catch((err: HttpError) => {
next(createHttpError(500, err));
});
} else {
next(createHttpError(400, 'please make sure your URL is of the format <contentType>/<mongoId>'));
}
};
The interfaces:
IImage
interface IImage {
// interface IImage extends IPieceofContent {
firstName: string;
lastName: string;
email: string;
phoneNumber: string;
IP: string;
customData?: object;
URL: string;
caption: string;
}
export {
IImage
};
ILetter
interface ILetter {
// interface ILetter extends IPieceofContent {
firstName: string;
lastName: string;
email: string;
phoneNumber: string;
IP: string;
customData?: object;
twitterHandle: string;
title: string;
description: string;
town: string;
}
export {
ILetter
};
IVideo
interface IVideo {
// interface IVideo extends IPieceofContent {
firstName: string;
lastName: string;
email: string;
phoneNumber: string;
IP: string;
customData?: object;
URL: string;
caption: string;
}
export {
IVideo
};
And the schemas:
imageModel
import {Schema, model} from 'mongoose';
import { IImage } from '../interfaces/IImage';
// For video and image they can use the same schema, but they are different models
const imageSchema = new Schema<IImage>({
firstName: {
required: true,
type: String
},
lastName: {
required: true,
type: String
},
email: {
required: true,
type: String
},
phoneNumber: {
required: true,
type: String
},
URL: {
required:true,
type: String
},
caption: {
required:true,
type: String
},
IP: {
required:true,
type: String
},
// has a type of Mixed
customData: {},
},
{ timestamps: true }
);
const imageModel = model<IImage>('images', imageSchema);
export {
imageModel
};
letterModel
import { ILetter } from '../interfaces/ILetter';
import { Schema, model } from 'mongoose';
const letterSchema = new Schema<ILetter>({
firstName: {
required: true,
type: String
},
lastName: {
required: true,
type: String
},
email: {
required: true,
type: String
},
phoneNumber: {
required: true,
type: String
},
twitterHandle: {
required: false,
type: String
},
title: {
required: true,
type: String
},
description: {
required: true,
type: String
},
town: {
required: true,
type: String
},
IP: {
required: true,
type: String
},
// has a type of Mixed
customData: {},
},
{ timestamps: true }
);
const letterModel = model<ILetter>('letters', letterSchema);
export {
letterModel
};
videoModel
import {Schema, model} from 'mongoose';
import { IVideo } from '../interfaces/IVideo';
const videoSchema = new Schema<IVideo>({
firstName: {
required: true,
type: String
},
lastName: {
required: true,
type: String
},
email: {
required: true,
type: String
},
phoneNumber: {
required: true,
type: String
},
URL: {
required:true,
type: String
},
caption: {
required:true,
type: String
},
IP: {
required:true,
type: String
},
// has a type of Mixed
customData: {},
},
{ timestamps: true }
);
const videoModel = model<IVideo>('videos', videoSchema);
export {
videoModel
};
TL:DR: trying to select between three content types based on info in request, idk how best to do this, plz help.

How to make oneOf field nullable with typescript and ajv

I have field called platform which can either be a string or a string[] this field can also be nullable/undefined (not passed).
typescript interface
export interface IEntityLeaderboardQuery {
rank: string;
entity_types: string[] | string;
country?: string | undefined;
region?: string | undefined;
start_date: string;
end_date: string;
top?: string | undefined;
platform?: string[] | string | undefined;
}
json schema
export const EntityLeaderboardQuerySchema: JSONSchemaType<IEntityLeaderboardQuery> = {
type: "object",
properties: {
rank: { type: "string" },
entity_types: {
oneOf: [
{
type: "string",
},
{
type: "array",
items: { type: "string" },
},
],
},
country: { type: "string", nullable: true },
region: { type: "string", nullable: true },
start_date: { type: "string" },
end_date: { type: "string" },
top: { type: "string", nullable: true },
platform: {
oneOf: [
{
type: "string",
nullable: true,
},
{
type: "array",
items: { type: "string" },
nullable: true,
},
],
},
},
required: ["rank", "entity_types", "start_date", "end_date"],
additionalProperties: false,
};
As you can see ive attempted to add nullable field to both objects within the oneOf array. However there is still an issue with the types
[ERROR] 11:07:49 ⨯ Unable to compile TypeScript:
src/api/leaderboard/entity/entity-leaderboard.interface.ts:43:14 - error TS2322: Type '{ type: "object"; properties: { rank: { type: "string"; }; entity_types: { oneOf: ({ type: "string"; } | { type: "array"; items: { type: "string"; }; })[]; }; country: { type: "string"; nullable: true; }; region: { ...; }; start_date: { ...; }; end_date: { ...; }; top: { ...; }; platform: { ...; }; }; required: ("ra...' is not assignable to type 'UncheckedJSONSchemaType<IEntityLeaderboardQuery, false>'.
Type '{ type: "object"; properties: { rank: { type: "string"; }; entity_types: { oneOf: ({ type: "string"; } | { type: "array"; items: { type: "string"; }; })[]; }; country: { type: "string"; nullable: true; }; region: { ...; }; start_date: { ...; }; end_date: { ...; }; top: { ...; }; platform: { ...; }; }; required: ("ra...' is not assignable to type '{ type: "object"; additionalProperties?: boolean | UncheckedJSONSchemaType<unknown, false> | undefined; unevaluatedProperties?: boolean | UncheckedJSONSchemaType<unknown, false> | undefined; ... 7 more ...; maxProperties?: number | undefined; } & { ...; } & { ...; } & { ...; }'.
Type '{ type: "object"; properties: { rank: { type: "string"; }; entity_types: { oneOf: ({ type: "string"; } | { type: "array"; items: { type: "string"; }; })[]; }; country: { type: "string"; nullable: true; }; region: { ...; }; start_date: { ...; }; end_date: { ...; }; top: { ...; }; platform: { ...; }; }; required: ("ra...' is not assignable to type '{ type: "object"; additionalProperties?: boolean | UncheckedJSONSchemaType<unknown, false> | undefined; unevaluatedProperties?: boolean | UncheckedJSONSchemaType<unknown, false> | undefined; ... 7 more ...; maxProperties?: number | undefined; }'.
The types of 'properties.platform' are incompatible between these types.
Type '{ oneOf: ({ type: "string"; nullable: boolean; } | { type: "array"; items: { type: "string"; nullable: boolean; }; nullable: boolean; })[]; }' is not assignable to type '{ $ref: string; } | (UncheckedJSONSchemaType<string | string[] | undefined, false> & { nullable: true; const?: undefined; enum?: readonly (string | string[] | null | undefined)[] | undefined; default?: string | ... 2 more ... | undefined; })'.
Type '{ oneOf: ({ type: "string"; nullable: boolean; } | { type: "array"; items: { type: "string"; nullable: boolean; }; nullable: boolean; })[]; }' is not assignable to type '{ type: "array"; items: UncheckedJSONSchemaType<string, false>; contains?: UncheckedPartialSchema | undefined; minItems?: number | undefined; ... 4 more ...; additionalItems?: undefined; } & { ...; } & { ...; } & { ...; }'.
Type '{ oneOf: ({ type: "string"; nullable: boolean; } | { type: "array"; items: { type: "string"; nullable: boolean; }; nullable: boolean; })[]; }' is missing the following properties from type '{ type: "array"; items: UncheckedJSONSchemaType<string, false>; contains?: UncheckedPartialSchema | undefined; minItems?: number | undefined; ... 4 more ...; additionalItems?: undefined; }': type, items
43 export const EntityLeaderboardQuerySchema: JSONSchemaType = {
ajv: 8.6.2
typescipt: 4.3.5
https://replit.com/join/ngjynszvjr-kaykhan
I had a similar situation and this worked for me:
oneOf: [
{
type: "string",
},
{
type: "array",
items: { type: "string" },
},
{
type: "object",
nullable: true,
},
],
Using { type: "null" } did not work.
It doesn't compile as typescript thinks it's trying to assign an optional type to a non-optional type.
The problem arises since you are using the assignment of nullable: true to the items of oneOf, you must specify outside since the property is using optional chaining ? platform?: string[] | string
platform: {
nullable: true, //<-- nullable Here
type: ['string', 'array'], //<-- when using "nullable" you must specify the types
oneOf: [
{
type: 'string'
},
{
type: 'array',
items: { type: 'string' },
}
]
}
If you are using strict mode with Ajv you will probably get a warning:
strict mode: use allowUnionTypes to allow union type keyword at "#/properties/platform" (strictTypes)
Enabling allowUnionTypes should work:
const ajv = new Ajv({ allowUnionTypes: true })

Issue with Date and Mongoose Typescript

I'm facing the issue with the Mongoose official document founded here.
import { Schema, Model, model } from 'mongoose';
export interface IUser {
name: string;
email: string;
avatar?: string;
created: Date;
}
const schema = new Schema<IUser, Model<IUser>, IUser>({
name: { type: String, required: true },
email: String,
avatar: String,
created: { type: Date, default: Date.now },
});
export const UserModel = model<IUser>('User', schema);
My problem is the created type in IUser is not the same as in schema and got the error:
Type '{ type: DateConstructor; default: () => number; }' is not assignable to type 'typeof SchemaType | Schema<any, any, undefined, unknown> | Schema<any, any, undefined, unknown>[] | readonly Schema<any, any, undefined, unknown>[] | Function[] | ... 6 more ... | undefined'.
Types of property 'type' are incompatible.
Type 'DateConstructor' is not assignable to type 'Date | typeof SchemaType | Schema<any, any, undefined, unknown> | undefined'.
Type 'DateConstructor' is missing the following properties from type 'typeof SchemaType': cast, checkRequired, set, getts(2322)
(property) created?: typeof SchemaType | Schema<any, any, undefined, unknown> | Schema<any, any, undefined, unknown>[] | readonly Schema<any, any, undefined, unknown>[] | Function[] | ... 6 more ... | undefined
Please let me know how to fix this.
Date.now() is a function which returns number. Instead of it, try using new Date() only. Also need to make changes in the type of createdAt to Number.
In the given doc link, createdAt field type is number but here you have written Date.
interface User {
name: string;
email: string;
avatar?: string;
createdAt: number;
}
OR
createdAt and updatedAt are timestamps which can be used directly without specifying in the schema.
const schema = new Schema<IUser, Model<IUser>, IUser>({
name: { type: String, required: true },
email: String,
avatar: String
},{
timestamps: true
});
I think this is working
import { Schema,Document } from 'mongoose';
export interface IUser {
name: string;
email: string;
avatar?: string;
created: Date;
}
const schema = new Schema<IUser>({
name: { type: String, required: true },
email: String,
avatar: String,
created: { type: Date, default: Date.now },
});
export const UserModel = model<IUser>('User', schema);

Property 'save' does not exist on type 'Ipub[]' | mongoose - typescript

i'm trying to update a schema in my backend app (with node/express/typescript), but when i do .save() it breaks because the type is not like type it's type[]
And in my app is happening the following error
Property 'save' does not exist on type 'Ipub[]'
This is the schema
import { Schema, model } from "mongoose";
import { Ipub } from "../helpers/PublicationInterfaces";
const publication = new Schema<Ipub>({
body: { type: String },
photo: { type: String },
creator: {
name: { required: true, type: String },
perfil: { type: String },
identifier: { required: true, type: String }
},
likes: [
{
identifier: { type: String }
}
],
comments: [
{
body: { type: String },
name: { type: String },
perfil: { type: String },
identifier: { type: String },
createdAt: { type: Date, default: new Date() },
likesComments: [
{
identifier: { type: String }
}
]
}
],
createdAt: { required: true, type: Date, default: new Date() }
});
export default model("Publications", publication);
This is schema's interface
import { Document } from "mongoose";
// Publication's representation
export interface Ipub extends Document {
body: string;
photo: string;
creator: {
name: string;
perfil?: string;
identifier: string;
};
likes?: [
{
identifier: string;
}
];
comments?: [
{
body: string;
name: string;
perfil: string;
identifier: string;
createdAt: Date;
likesComments: [
{
identifier: string;
}
];
}
];
createdAt: Date;
}
And this is where the code breaks, look the line of code where i put await updateSub.save()
// Look for user's publications
const usersPub: Ipub[] = await Publication.find();
const updatePub = usersPub.filter(
(p: Ipub) => p.creator.identifier === specificUser.id
);
// update photo
try {
if (banner !== "") {
specificUser.banner = banner;
} else if (perfil !== "") {
specificUser.perfil = perfil;
updatePub.map((pub: Ipub) => (pub.creator.perfil = perfil));
await updatePub.save();
}
await specificUser.save();
return res.json(specificUser);
} catch (err) {
console.log(err);
return res.status(500).json({ Error: "The API Failed" });
}
It's really weird, it looks like it's because of the Ipub[] type definition, what can i do about it !
Thanks for your help !

Node js + Objection js + Postgresql. Argument of type '{ token: string }' is not assignable to parameter of type 'PartialUpdate<User>'

Environment:
node js
ES6
knex: ^0.16.3
objection: ^1.5.3
pg: ^7.8.0 ~ postgresql
Problem:
I can't update user token in the database. I get an error message from typescript.
Typescript error message:
Argument of type '{ token: string; }' is not assignable to parameter
of type 'PartialUpdate<User>'. Object literal may only specify known
properties, and 'token' does not exist in type 'PartialUpdate<User>'.
Problem method
If I write #ts-ignore, the method will work, but I can't understand.
Why does it give me an error?
import { User } from '#database/models';
...
const setToken = async (id: any, token: string) => {
try {
await transaction(User.knex(), trx =>
User.query(trx)
// An error appears on this line
.update({ token })
.where({ id }),
);
} catch (e) {
throw e;
}
};
My user model
'use strict';
import { Model } from 'objection';
export default class User extends Model {
static get tableName() {
return 'users';
}
static get jsonSchema() {
return {
type: 'object',
properties: {
id: { type: 'uuid' },
full_name: { type: 'string', minLength: 1, maxLength: 255 },
email: { type: 'string', minLength: 1, maxLength: 255 },
avatar: { type: 'string' },
provider_data: {
type: 'object',
properties: {
uid: { type: 'string' },
provider: { type: 'string' },
},
},
token: { type: 'string' },
created_at: { type: 'timestamp' },
updated_at: { type: 'timestamp' },
},
};
}
}
The problem was that I did not define the types of variables in my model. An example from the official library that gave me know about what I did wrong - https://github.com/Vincit/objection.js/tree/master/examples/express-ts
Updated model
export default class User extends Model {
readonly id!: v4;
full_name?: string;
email?: string;
avatar?: string;
provider_data?: {
uid: string;
provider: string;
};
token?: string;
static tableName = 'users';
static jsonSchema = {
type: 'object',
properties: {
id: { type: 'uuid' },
full_name: { type: 'string', minLength: 1, maxLength: 255 },
email: { type: 'string', minLength: 1, maxLength: 255 },
avatar: { type: 'string' },
provider_data: {
type: 'object',
properties: {
uid: { type: 'string' },
provider: { type: 'string' },
},
},
token: { type: 'string' },
created_at: { type: 'timestamp' },
updated_at: { type: 'timestamp' },
},
};
}
Updated method
const setToken = async (id: any, token: string) => {
try {
User.query()
.update({ token })
.where({ id });
} catch (e) {
throw e;
}
};
**please provide code with variable partialUpdate. because you are having an error because of the wrong declaration of the type of variable partialUpdate. TypeScript fully focused on variable Type and if the Type of the variable does not match the content you are providing to that variable type error generates. you are passing object type value {token:string} to your variable partialUpdate which can only hold string type variable when you declared it. **
PartialUpdate:Object
or
PartialUpdate = {}
will solve the problem.

Resources