pass a model instance to a function - node.js

This is the first time I am using sequelize (v6) and generally JS. and a little Confused. I have this function:
function userStandard(user: User, currentlyActiveRate: number){
}
//User is now
import { DataTypes } from "sequelize";
import sequelize from '../../../sequelize';
export const User = sequelize.define('User', {
email: {
type: DataTypes.STRING },
password_hash: {
type: DataTypes.STRING,
}
}, {
timestamps: true,
freezeTableName: false
});
I can't run/compile the app now with the following error:
'User' refers to a value, but is being used as a type here. Did you mean 'typeof User'?
if i do user: typeof User
it compiles but what is exactly the issue? Why doesn't it work without typeof?

You should declare an interface for your model as well as a type to describe a constructor and call define as a generic method:
import { Model, DataTypes, BuildOptions } from 'sequelize';
export interface UserModel extends Model {
email: string;
password_hash: string;
}
export type UserModelStatic = typeof Model
& { new(values?: Record<string, unknown>, options?: BuildOptions): UserModel }
const User = <UserModelStatic>sequelize.define('User', {
email: {
type: DataTypes.STRING },
password_hash: {
type: DataTypes.STRING,
}
}, {
timestamps: true,
freezeTableName: false
});
export { User }

Related

What type should I assign to the parameter so that it does not give me this error?

import { Model, DataTypes } from 'sequelize';
interface IExtracciones {
id_extraccion: Number | null | undefined;
monto_extraido: Number;
fecha_de_extraccion: Date;
numero_de_cuenta: Number;
}
export default class Extracciones extends Model<IExtracciones> {}
Extracciones.init(
{
id_extraccion: {
type: DataTypes.INTEGER,
primaryKey: true,
},
monto_extraido: {
type: DataTypes.INTEGER,
allowNull: false,
},
fecha_de_extraccion: {
type: DataTypes.DATE,
allowNull: false,
},
numero_de_cuenta: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
sequelize: bd,
tableName: 'extracciones',
}
);
function agregaRelacion(m1: typeof Model /*error here*/, m2: any, fk: String) {
m1.hasOne(m2, { foreignKey: fk });
m2.hasOne(m1, { foreignKey: fk });
}
The 'this' context of type 'typeof Model' is not assignable to method's 'this' of type 'ModelStatic<Model<{}, {}>>'.
Type 'typeof Model' is not assignable to type 'new () => Model<{}, {}>'.
Cannot assign an abstract constructor type to a non-abstract constructor type.ts(2684)
import { Model, DataTypes, Sequelize, ModelStatic } from 'sequelize';
interface IExtracciones {
id_extraccion: Number | null | undefined;
monto_extraido: Number;
fecha_de_extraccion: Date;
numero_de_cuenta: Number;
}
export default class Extracciones extends Model<IExtracciones> {}
Extracciones.init(
{
id_extraccion: {
type: DataTypes.INTEGER,
primaryKey: true,
},
monto_extraido: {
type: DataTypes.INTEGER,
allowNull: false,
},
fecha_de_extraccion: {
type: DataTypes.DATE,
allowNull: false,
},
numero_de_cuenta: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
// change new Sequeilze()
sequelize: new Sequelize(),
tableName: 'extracciones',
}
);
// m2 replace any with appropriate modelname if needed
/* corrected fk type (String compiles to javascript, shouldn't used as type) */
function agregaRelacion(m1: ModelStatic<Extracciones>, m2: ModelStatic<any>, fk: string) {
m1.hasOne(m2, { foreignKey: fk });
m2.hasOne(m1, { foreignKey: fk });
}
Solution
imagine we have a users table and our users have skills so it is a one to many
right?
UserModel
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface UserAttributes {
id: number;
name: string;
email: string;
password: string;
middleName: string;
lastName: string;
secondLastName: string;
active: boolean;
createdAt?: Date;
updatedAt?: Date;
}
export interface UserModel extends Model<UserAttributes>, UserAttributes {}
export class User extends Model<UserModel, UserAttributes> {}
export type UserStatic = typeof Model & {
new (values?: object, options?: BuildOptions): UserModel;
};
export function UserFactory (sequelize: Sequelize) {
return <UserStatic>sequelize.define("users", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
password: {
type: DataTypes.STRING,
allowNull: false,
},
middleName: {
type: DataTypes.STRING,
allowNull: true,
},
lastName: {
type: DataTypes.STRING,
allowNull: false,
},
secondLastName: {
type: DataTypes.STRING,
allowNull: true,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
});
}
SkillsModel
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface SkillsAttributes {
skill: string;
}
export interface SkillsModel
extends Model<SkillsAttributes>,
SkillsAttributes {}
export type SkillsStatic = typeof Model & {
new (values?: object, options?: BuildOptions): SkillsModel;
};
export function SkillsFactory (sequelize: Sequelize) {
return <SkillsStatic>sequelize.define("skills", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
skill: {
type: DataTypes.STRING,
unique: true,
},
});
}
now imagine we have a dir call anyName and in there we have
we are going to export users and skills from our index file
index
import { Sequelize } from "sequelize";
import { CourseFactory, CourseStatic } from "./courses";
import { EducationFactory, EducationStatic } from "./education";
import { ExperienceFactory, ExperienceStatic } from "./experience";
import { FieldsFactory, FieldsStatic } from "./fields-of-interets";
import { GeneralFactory, GeneralStatic } from "./general";
import { SkillsFactory, SkillsStatic } from "./skills";
import { SkillsTypeFactory, SkillsTypeStatic } from "./skills-type";
import { SocialMediaFactory, SocialMediaStatic } from "./social-media";
import { UserFactory, UserStatic } from "./users";
export interface DB {
sequelize: Sequelize;
User: UserStatic;
Skills: SkillsStatic;
SkillsType: SkillsTypeStatic;
Experience: ExperienceStatic;
Education: EducationStatic;
Course: CourseStatic;
General: GeneralStatic;
SocialMedia: SocialMediaStatic;
FieldsOfInterest: FieldsStatic;
}
const sequelize = new Sequelize(
(process.env.DB_NAME = "rest_resume_api"),
(process.env.DB_USER = "john"),
(process.env.DB_PASSWORD = "password"),
{
port: Number(process.env.DB_PORT) || 54320,
host: process.env.DB_HOST || "localhost",
dialect: "postgres",
pool: {
min: 0,
max: 5,
acquire: 30000,
idle: 10000,
},
}
);
const User = UserFactory(sequelize);
const Skills = SkillsFactory(sequelize);
const SkillsType = SkillsTypeFactory(sequelize);
const Experience = ExperienceFactory(sequelize);
const Education = EducationFactory(sequelize);
const Course = CourseFactory(sequelize);
const General = GeneralFactory(sequelize);
const SocialMedia = SocialMediaFactory(sequelize);
const FieldsOfInterest = FieldsFactory(sequelize);
SkillsType.belongsTo(Skills);
SkillsType.belongsToMany(User, { through: "users_has_skills" });
User.belongsToMany(SkillsType, { through: "users_has_skills" });
Experience.belongsToMany(User, { through: "user_has_experience" });
User.belongsToMany(Experience, { through: "user_has_experience" });
Education.belongsToMany(User, { through: "user_has_education" });
User.belongsToMany(Education, { through: "user_has_education" });
Course.belongsTo(User);
General.belongsTo(User);
SocialMedia.belongsToMany(User, { through: "user_has_social" });
User.belongsToMany(SocialMedia, { through: "user_has_social" });
FieldsOfInterest.belongsToMany(User, { through: "user_has_fields" });
User.belongsToMany(FieldsOfInterest, { through: "user_has_fields" });
export const db: DB = {
sequelize,
User,
Skills,
};

How to create type safety for sequelize many to many associations methods, like (setModels, etc.)

I have model
model/Caregiver
import { BuildOptions, DataTypes, Model, Sequelize } from 'sequelize';
export interface CaregiverAttributes {
caregiver_id: number | string;
caregiver_notification_preferences?: string;
}
export interface CaregiverModel
extends Model<CaregiverAttributes>,
CaregiverAttributes {}
export type CaregiverStatic = typeof Model & {
new (values?: object, options?: BuildOptions): CaregiverModel;
};
export function CaregiverFactory(sequelize: Sequelize): CaregiverStatic {
return <CaregiverStatic>sequelize.define(
'caregiver',
{
caregiver_id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false,
primaryKey: true,
},
caregiver_notification_preferences: {
type: DataTypes.STRING,
allowNull: true,
},
},
{
timestamps: false,
underscored: true,
tableName: 'caregiver',
}
);
}
and associations
Caregiver.belongsToMany(Patient, {
foreignKey: 'caregiver_id',
targetKey: 'caregiver_id',
as: 'Patient',
});
But when I try to get or set
const patients = await caregiver.getPatients();
I have an error: property getPatients does not exist on type CaregiverModel
Maybe someone have the same problem?
Notice: With // #ts-ignore works fine.
Solved
Find this paper https://vivacitylabs.com/setup-typescript-sequelize/ , where it is describe.

How to use TypeScript with Sequelize

I already have my server application written in Node, PostgreSQL, Sequelize using Fastify.
Now I would like to use TypeScript. Can anyone tell me how to begin rewriting my Server application using TypeScript.
Updated
/**
* Keep this file in sync with the code in the "Usage" section
* in /docs/manual/other-topics/typescript.md
*
* Don't include this comment in the md file.
*/
import {
Association, DataTypes, HasManyAddAssociationMixin, HasManyCountAssociationsMixin,
HasManyCreateAssociationMixin, HasManyGetAssociationsMixin, HasManyHasAssociationMixin,
HasManySetAssociationsMixin, HasManyAddAssociationsMixin, HasManyHasAssociationsMixin,
HasManyRemoveAssociationMixin, HasManyRemoveAssociationsMixin, Model, ModelDefined, Optional,
Sequelize, InferAttributes, InferCreationAttributes, CreationOptional, NonAttribute, ForeignKey,
} from 'sequelize';
const sequelize = new Sequelize('mysql://root:asd123#localhost:3306/mydb');
// 'projects' is excluded as it's not an attribute, it's an association.
class User extends Model<InferAttributes<User, { omit: 'projects' }>, InferCreationAttributes<User, { omit: 'projects' }>> {
// id can be undefined during creation when using `autoIncrement`
declare id: CreationOptional<number>;
declare name: string;
declare preferredName: string | null; // for nullable fields
// timestamps!
// createdAt can be undefined during creation
declare createdAt: CreationOptional<Date>;
// updatedAt can be undefined during creation
declare updatedAt: CreationOptional<Date>;
// Since TS cannot determine model association at compile time
// we have to declare them here purely virtually
// these will not exist until `Model.init` was called.
declare getProjects: HasManyGetAssociationsMixin<Project>; // Note the null assertions!
declare addProject: HasManyAddAssociationMixin<Project, number>;
declare addProjects: HasManyAddAssociationsMixin<Project, number>;
declare setProjects: HasManySetAssociationsMixin<Project, number>;
declare removeProject: HasManyRemoveAssociationMixin<Project, number>;
declare removeProjects: HasManyRemoveAssociationsMixin<Project, number>;
declare hasProject: HasManyHasAssociationMixin<Project, number>;
declare hasProjects: HasManyHasAssociationsMixin<Project, number>;
declare countProjects: HasManyCountAssociationsMixin;
declare createProject: HasManyCreateAssociationMixin<Project, 'ownerId'>;
// You can also pre-declare possible inclusions, these will only be populated if you
// actively include a relation.
declare projects?: NonAttribute<Project[]>; // Note this is optional since it's only populated when explicitly requested in code
// getters that are not attributes should be tagged using NonAttribute
// to remove them from the model's Attribute Typings.
get fullName(): NonAttribute<string> {
return this.name;
}
declare static associations: {
projects: Association<User, Project>;
};
}
class Project extends Model<
InferAttributes<Project>,
InferCreationAttributes<Project>
> {
// id can be undefined during creation when using `autoIncrement`
declare id: CreationOptional<number>;
// foreign keys are automatically added by associations methods (like Project.belongsTo)
// by branding them using the `ForeignKey` type, `Project.init` will know it does not need to
// display an error if ownerId is missing.
declare ownerId: ForeignKey<User['id']>;
declare name: string;
// `owner` is an eagerly-loaded association.
// We tag it as `NonAttribute`
declare owner?: NonAttribute<User>;
// createdAt can be undefined during creation
declare createdAt: CreationOptional<Date>;
// updatedAt can be undefined during creation
declare updatedAt: CreationOptional<Date>;
}
class Address extends Model<
InferAttributes<Address>,
InferCreationAttributes<Address>
> {
declare userId: ForeignKey<User['id']>;
declare address: string;
// createdAt can be undefined during creation
declare createdAt: CreationOptional<Date>;
// updatedAt can be undefined during creation
declare updatedAt: CreationOptional<Date>;
}
Project.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
name: {
type: new DataTypes.STRING(128),
allowNull: false
},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{
sequelize,
tableName: 'projects'
}
);
User.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
name: {
type: new DataTypes.STRING(128),
allowNull: false
},
preferredName: {
type: new DataTypes.STRING(128),
allowNull: true
},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{
tableName: 'users',
sequelize // passing the `sequelize` instance is required
}
);
Address.init(
{
address: {
type: new DataTypes.STRING(128),
allowNull: false
},
createdAt: DataTypes.DATE,
updatedAt: DataTypes.DATE,
},
{
tableName: 'address',
sequelize // passing the `sequelize` instance is required
}
);
// You can also define modules in a functional way
interface NoteAttributes {
id: number;
title: string;
content: string;
}
// You can also set multiple attributes optional at once
type NoteCreationAttributes = Optional<NoteAttributes, 'id' | 'title'>;
// And with a functional approach defining a module looks like this
const Note: ModelDefined<
NoteAttributes,
NoteCreationAttributes
> = sequelize.define(
'Note',
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true
},
title: {
type: new DataTypes.STRING(64),
defaultValue: 'Unnamed Note'
},
content: {
type: new DataTypes.STRING(4096),
allowNull: false
}
},
{
tableName: 'notes'
}
);
// Here we associate which actually populates out pre-declared `association` static and other methods.
User.hasMany(Project, {
sourceKey: 'id',
foreignKey: 'ownerId',
as: 'projects' // this determines the name in `associations`!
});
Address.belongsTo(User, { targetKey: 'id' });
User.hasOne(Address, { sourceKey: 'id' });
async function doStuffWithUser() {
const newUser = await User.create({
name: 'Johnny',
preferredName: 'John',
});
console.log(newUser.id, newUser.name, newUser.preferredName);
const project = await newUser.createProject({
name: 'first!'
});
const ourUser = await User.findByPk(1, {
include: [User.associations.projects],
rejectOnEmpty: true // Specifying true here removes `null` from the return type!
});
// Note the `!` null assertion since TS can't know if we included
// the model or not
console.log(ourUser.projects![0].name);
}
(async () => {
await sequelize.sync();
await doStuffWithUser();
})();
OLD
Using Decorators is something you should avoid as much as possible, they are not ECMAScript standard. They are even consider legacy. It is why I'm going to show you how to use sequelize with typescript.
we just need to follow the docs: https://sequelize.org/v5/manual/typescript.html but as it is not very clear, or at least to me. It took me a while understand it.
There it says that you need to install this tree things
* #types/node
* #types/validator // this one is not need it
* #types/bluebird
npm i -D #types/node #types/bluebird
then let's assume your project looks like so:
myProject
--src
----models
------index.ts
------user-model.ts
------other-model.ts
----controllers
----index.ts
--package.json
Let's create the user model first
`./src/models/user-model.ts`
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface UserAttributes {
id: number;
name: string;
email: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface UserModel extends Model<UserAttributes>, UserAttributes {}
export class User extends Model<UserModel, UserAttributes> {}
export type UserStatic = typeof Model & {
new (values?: object, options?: BuildOptions): UserModel;
};
export function UserFactory (sequelize: Sequelize): UserStatic {
return <UserStatic>sequelize.define("users", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
name: {
type: DataTypes.STRING,
allowNull: false,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
});
}
Now just to play arrow let's create another-model.ts
`./src/models/another-model.ts`
import { BuildOptions, DataTypes, Model, Sequelize } from "sequelize";
export interface SkillsAttributes {
id: number;
skill: string;
createdAt?: Date;
updatedAt?: Date;
}
export interface SkillsModel extends Model<SkillsAttributes>, SkillsAttributes {}
export class Skills extends Model<SkillsModel, SkillsAttributes> {}
export type SkillsStatic = typeof Model & {
new (values?: object, options?: BuildOptions): SkillsModel;
};
export function SkillsFactory (sequelize: Sequelize): SkillsStatic {
return <SkillsStatic>sequelize.define("skills", {
id: {
type: DataTypes.INTEGER,
autoIncrement: true,
primaryKey: true,
},
skill: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
createdAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
updatedAt: {
type: DataTypes.DATE,
allowNull: false,
defaultValue: DataTypes.NOW,
},
});
}
Our Entities are done. now the db connection.
open ./src/models/index.ts there is where we gonna place the seqelize instance
`./src/models/index.ts`
import * as sequelize from "sequelize";
import {userFactory} from "./user-model";
import {skillsFactory} from "./other-model";
export const dbConfig = new sequelize.Sequelize(
(process.env.DB_NAME = "db-name"),
(process.env.DB_USER = "db-user"),
(process.env.DB_PASSWORD = "db-password"),
{
port: Number(process.env.DB_PORT) || 54320,
host: process.env.DB_HOST || "localhost",
dialect: "postgres",
pool: {
min: 0,
max: 5,
acquire: 30000,
idle: 10000,
},
}
);
// SOMETHING VERY IMPORTANT them Factory functions expect a
// sequelize instance as parameter give them `dbConfig`
export const User = userFactory(dbConfig);
export const Skills = skillsFactory(dbConfig);
// Users have skills then lets create that relationship
User.hasMay(Skills);
// or instead of that, maybe many users have many skills
Skills.belongsToMany(Users, { through: "users_have_skills" });
// the skill is the limit!
on our index.ts add, if you just want to open connection
db.sequelize
.authenticate()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
or if you want to create them tables
db.sequelize
.sync()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
some like this
import * as bodyParser from "body-parser";
import * as express from "express";
import { dbConfig } from "./models";
import { routes } from "./routes";
import { logger } from "./utils/logger";
import { timeMiddleware } from "./utils/middlewares";
export function expressApp () {
dbConfig
.authenticate()
.then(() => logger.info("connected to db"))
.catch(() => {
throw "error";
});
const app: Application = express();
if (process.env.NODE_ENV === "production") {
app.use(require("helmet")());
app.use(require("compression")());
} else {
app.use(require("cors")());
}
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true, limit: "5m" }));
app.use(timeMiddleware);
app.use("/", routes(db));
return app;
}
Once again the sky is the limit.
If you do this you'll have all the power of the autocomplete.
here an example: https://github.com/EnetoJara/resume-app
Use sequelize-typescript. Convert your tables and views into a class that extends Model object.
Use annotations in classes for defining your table.
import {Table, Column, Model, HasMany} from 'sequelize-typescript';
#Table
class Person extends Model<Person> {
#Column
name: string;
#Column
birthday: Date;
#HasMany(() => Hobby)
hobbies: Hobby[];
}
Create a connection to DB by creating the object:
const sequelize = new Sequelize(configuration...).
Then register your tables to this object.
sequelize.add([Person])
For further reference check this module.
Sequelize-Typescript

VSCode incorrectly inferring return type of Sequelize findOne and findAll as any

I have a model defined as:
import { Sequelize, Model, BuildOptions, DataTypes } from 'sequelize';
interface User extends Model {
readonly id: string;
email: string;
name: string;
password_hash: string;
readonly created_at: Date;
readonly updated_at: Date;
}
type UserStatic = typeof Model & {
new (values?: Partial<User>, options?: BuildOptions): User;
};
export function build(seq: Sequelize) {
const User = seq.define(
'User',
{
id: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
primaryKey: true,
allowNull: false,
},
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true,
},
password_hash: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
timestamps: true,
createdAt: 'created_at',
updatedAt: 'updated_at',
}
) as UserStatic;
return User;
}
As is defined in the docs.
But, when I use this model, I get the resulting type of findAll and findOne as:
(method) Model<T = any, T2 = any>.findAll<User>(this: (new () => User) & typeof Model, options?: FindOptions | undefined): any
(method) Model<T = any, T2 = any>.findOne<User>(this: (new () => User) & typeof Model, options?: FindOptions | undefined): any (+1 overload)
But going to definition, I see:
public static findAll<M extends Model>(this: { new (): M } & typeof Model, options?: FindOptions): Promise<M[]>;
public static findOne<M extends Model>(
this: { new (): M } & typeof Model,
options?: FindOptions
): Promise<M | null>;
public static findOne<M extends Model>(this: { new (): M } & typeof Model, options: NonNullFindOptions): Promise<M>;
So, as can be seen, the type of M is inferred correctly, but the return type is turning from Promise<M | null> or Promise<M[]> to any
Anyone knows how can I fix this and get the correct type directly?
Information:
VSCode: 1.42.0
Typescript: 3.7.5
Sequelize: 5.21.4
I've managed a workaround by explicit casting the return type
const user = (await UserModel.findAll({
where: {
email: args.input.email,
},
})) as User | null;
But that is extra work I don't want to have each time I use a sequelize model
Found it, I forgot to add #types/bluebird and #types/validator

Creating instance methods in a Sequelize Model using Typescript

I want to extend a Sequelize Model class to add other instance methods but typescript keeps complaining that "Property 'prototype' does not exist on type 'Model'"
const MyModel = (sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes) => {
const User = sequelize.define<Instance, Attribute>(
"users",
{
id: {
type: dataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
email: {
type: dataTypes.STRING
},
...
},
{
tableName: "users",
...
},
);
User.prototype.verifyUser = function(password: string) {
...
};
return User;
};
I expect User.prototype.verifyUser to work but typescript complains. How to add to typings?
Following #Shadrech comment, I've an alternative (less hacky and abstract).
export interface UserAttributes {
...
}
export interface UserInstance extends Sequelize.Instance<UserAttributes>, UserAttributes {
}
interface UserModelInstanceMethods extends Sequelize.Model<UserInstance, UserAttributes> {
// Came to this question looking for a better approach to this
// You'll need root's definitions for invocation and prototype's for creation
verifyPassword: (password: string) => Promise<boolean>;
prototype: {
verifyPassword: (password: string) => Promise<boolean>;
};
}
const MyModel = (sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes): UserModelInstanceMethods => {
const User = sequelize.define<UserInstance, UserAttributes>(
...
) as UserModelInstanceMethods;
User.prototype.verifyUser = function(password: string) {
...
};
return User;
}
Using your model:
sequelize.query("SELECT ...").then((user: UserInstance & UserModelInstanceMethods) => {
user.verifyPassword(req.body.password) // <= from UserModelInstanceMethods
user.getDataValue('name') // <= from UserInstance
})
One solution I've seen is where you force type after declaring the Model. So
interface UserModelInstanceMethods extends Sequelize.Model<Instance, Attributes> {
prototype: {
verifyPassword: (password: string) => Promise<boolean>;
};
}
const MyModel = (sequelize: Sequelize.Sequelize, dataTypes: Sequelize.DataTypes) => {
const User = sequelize.define<Instance, Attribute>(
"users",
{
id: {
type: dataTypes.INTEGER,
primaryKey: true,
autoIncrement: true,
},
email: {
type: dataTypes.STRING
},
...
},
{
tableName: "users",
...
},
);
User.prototype.verifyUser = function(password: string) {
...
};
return User;
} as Sequelize.Model<Instance, Attributes> & UserModelInstanceMethods;
According to the main Sequelize TypeScript Doc, I think that the best way to implement it is using DataTypes.VIRTUAL and skip the property with TypeScript Omit utility on the model creation interface.
Important! Remember the Issue#11675!
https://stackoverflow.com/a/69289067/717267
https://github.com/sequelize/sequelize/issues/11675
A simple example:
import {
Sequelize,
Model,
ModelDefined,
DataTypes,
Optional,
// ...
} from 'sequelize';
interface ProjectAttributes {
id: number;
ownerId: number;
name: string;
readonly createdAt: Date;
readonly updatedAt: Date;
// #region Methods
myMethod(name: string): Promise<void>; // <<<===
// #endregion
}
interface ProjectCreationAttributes extends Omit< // <<<===
Optional<
ProjectAttributes,
| 'id'
| 'createdAt'
>,
'myMethod' // <<<===
> {}
class Project extends Model<ProjectAttributes, ProjectCreationAttributes>
implements ProjectAttributes {
public id: ProjectAttributes['id'];
public ownerId: ProjectAttributes['ownerId'];
public name: ProjectAttributes['name'];
public readonly createdAt: ProjectAttributes['createdAt'];
public readonly updatedAt: ProjectAttributes['updatedAt'];
public readonly myMethod: ProjectAttributes['myMethod'] // <<<===
/**
* Initialization to fix Sequelize Issue #11675.
*
* #see https://stackoverflow.com/questions/66515762/configuring-babel-typescript-for-sequelize-orm-causes-undefined-properties
* #see https://github.com/sequelize/sequelize/issues/11675
* #ref #SEQUELIZE-11675
*/
constructor(values?: TCreationAttributes, options?: BuildOptions) {
super(values, options);
// All fields should be here!
this.id = this.getDataValue('id');
this.ownerId = this.getDataValue('ownerId');
this.name = this.getDataValue('name');
this.createdAt = this.getDataValue('createdAt');
this.updatedAt = this.getDataValue('updatedAt');
this.myMethod = async (name) => { // <<<===
// Implementation example!
await this.update({
name,
});
};
}
// #region Methods
public toString() {
return `#${this.name} [${this.ownerId}] #${this.id}`;
}
// #endregion
}
Project.init(
{
id: {
type: DataTypes.INTEGER.UNSIGNED,
autoIncrement: true,
primaryKey: true,
},
ownerId: {
type: DataTypes.INTEGER.UNSIGNED,
allowNull: false,
},
name: {
type: new DataTypes.STRING(128),
allowNull: false,
},
myMethod: { // <<<===
type: DataTypes.VIRTUAL(DataTypes.ABSTRACT),
}
},
{
sequelize,
tableName: "projects",
}
);
Step 1:
Define a new type that will describe the definition of the model DefinedModel. In addition receive a generic T to get responses from database defined by an interface.
Step 2:
Create an instance of the model parsing the connection.define return to our DefinedModel.
// Step 0: Declarations
const connection: Sequelize = new Sequelize({...});
const modelName: string = '...';
const definition: ModelAttributes = {...};
const options: ModelOptions = {...};
interface MyInterface {...}; // Should describe table data
// Step 1
type DefinedModel<T> = typeof Model & {
new(values?: object, options?: BuildOptions): T;
}
// Step 2
const model: DefinedModel<Model> = <DefinedModel<Model>>connection.define(modelName, definition, options);
// Step 2 with Interface definition
const iModel: DefinedModel<MyInterface & Model> = <DefinedModel<MyInterface & Model>> connection.define(modelName, definition, options);

Resources