typescript-sequelize-example
typescript-sequelize-example copied to clipboard
Adding Instance And ClassLevel Methods
Hi Sir thank you for your awesome tutorial on sequelize and typescript please can you let me know how can we add InstanceLevel and ClassLevelMethods in your code i am trying for a day now but could not acheive the desired results :-
const User = sequelize.define('user', { firstname: Sequelize.STRING });
// Adding a class level method User.classLevelMethod = function() { return 'foo'; };
// Adding an instance level method User.prototype.instanceLevelMethod = function() { return 'bar'; }; Thank You
Hey @yaldram . Glad to hear the tutorial was helpful!
I've not got much time to look at this right now unfortunately - I'm deep in the middle of my masters exams at university.
According to the sequelize docs, it looks like the way you are adding class and instance level methods is correct.
But, my understanding would be that you'd also need to do the following. 1) add type signatures for the instance level methods to the UserInstance
interface. 2) somehow add type signatures for the class level methods to sequelize.model<UserInstance, UserAttributes>
.
We need to do these so that typescript knows that these methods exist.
I've been playing around and I think I've managed to get a solution (though, its untested).
We can define instance and class methods in a different way. sequelize.define<..>(...)
takes a third parameter: an options object. This has optional classMethods
and instanceMethods
fields which we can use as an alternative method to define class and instance methods.
const User = sequelize.define<UserInstance, UserAttributes>('User', attributes, {
classMethods: {
classLevelMethod(): string {
return 'foo';
}
},
instanceMethods: {
instanceLevelMethod(arg: number): number {
return this.countUpvotedComments() + arg;
}
}
})
Now, we still need to add the type signatures to the correct classes.
1) Instance Level Methods
This one is easier. We just need to add the correct type signature to the UserInstance
method.
export interface UserInstance extends Sequelize.Instance<UserAttributes>, UserAttributes {
instanceLevelMethod: (arg: number) => number;
// all the other signatures below ...
}
2) Class Level Methods
This turned out to be more tricky. We can't add types to the Sequelize.model<UserInstance, UserAttributes>
class ourselves, because that's part of sequelize. We don't want to add it to UserInstance
, because then the method is available on instances, not the class. We can't add it to UserAttributes
, because then we'd be saying that we want to specify the method every time we create a user, which is just confusing and not what we want.
So, I created a new interface UserModel
. This inherits from Sequelize.model<UserInstance, UserAttributes>
. I put this interface in models/User.ts
. We can add the method signature here.
export interface UserModel extends sequelize.Model<UserInstance, UserAttributes> {
classLevelMethod: () => String
};
Okay, now we need to make the User
model we create in UserFactory
actually have the type UserModel
, not Sequelize.Model<UserInstance, UserAttributes>
. We also need to assert to the complier ourselves that the call to sequelize.define('User', ...)
is indeed giving us an object with the methods defined in UserModel
too.
const User: UserModel = sequelize.define<UserInstance, UserAttributes>('User', attributes, {
classMethods: {
classLevelMethod(): string {
return 'foo'
}
},
instanceMethods: {
instanceLevelMethod(arg: number): number {
return this.countUpvotedComments() + arg;
}
}
}) as UserModel; // type assertion tells typescript to trust us and the return object of sequelize.define does indeed have the classLevelMethod function.
Finally, we need to make the UserFactory
's type say that its a UserModel
that's being returned, not a Sequelize.Model<UserInstance, UserFactory>
. We also need to update this type in our DbInterface
type, so that in app.ts
, the db
object thinks its db.User
is of type UserModel
too.
// models/User.ts
export const UserFactory = (sequelize: Sequelize.Sequelize, DataTypes: Sequelize.DataTypes): UserModel => { // **this return type has changed!
...
};
and
// typings/DbInterface/index.d.ts
...
import { UserModel } from 'models/User';
export interface DbInterface {
sequelize: Sequelize.Sequelize;
Sequelize: Sequelize.SequelizeStatic;
...
User: UserModel;
...
}
Conclusions...
Now, you should be able to get the new methods in app.ts
. For example, this compiles for me:
db.User.classLevelMethod();
db.User.findById(1).then(user => {
const x: number = user.instanceLevelMethod(10);
});
A few things to note.
You may want to do this for all your models, not just User.
If you can, it would be great to come up with a way to ensure that the sequelize.define
options object { classMethods: ..., instanceMethods: ... }
must have all the functions that are given in UserInstance
and UserModel
.
Setting up this way gets the types to work just fine. However, my experience says that this doesn't always mean that it will work. I haven't had time to test it (sorry ☹️).
Thank You sir for your awesome and helpful response, i tried using the above methods but the problem is its not showing on my instance both in ts and complied js, plus i have to reach deep into the model instance object for example :- user._modelOptions.instanceMethods.generateHas(password); like so. The problem for this is i guess that i am using Sequelize v4 and in the new version we do :-
NEW METHOD :-
// Class Method
Model.associate = function (models) {
...associate the models
};
// Instance Method
Model.prototype.someMethod = function () {..}
OLD METHOD :-
const Model = sequelize.define('Model', {
...
}, {
classMethods: {
associate: function (model) {...}
},
instanceMethods: {
someMethod: function () { ...}
}
});
If you have time please respond , Best of luck for your exams.
Hello! I know this is an outdated package but if this could be updated that would be great!