使用 Typescript 在 Sequelize 模型中创建实例方法
Posted
技术标签:
【中文标题】使用 Typescript 在 Sequelize 模型中创建实例方法【英文标题】:Creating instance methods in a Sequelize Model using Typescript 【发布时间】:2019-06-25 23:57:48 【问题描述】:我想扩展一个 Sequelize Model 类以添加其他实例方法,但 typescript 一直抱怨“类型 'Model' 上不存在属性 'prototype'”
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;
;
我希望 User.prototype.verifyUser
可以工作,但 typescript 会抱怨。如何添加到打字?
【问题讨论】:
【参考方案1】:根据主要的Sequelize TypeScript Doc,我认为最好的实现方式是在模型创建界面使用DataTypes.VIRTUAL
,跳过带有TypeScript Omit
utility的属性。
重要!记住Issue#11675!
https://***.com/a/69289067/717267 https://github.com/sequelize/sequelize/issues/11675一个简单的例子:
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://***.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",
);
【讨论】:
【参考方案2】:第 1 步:
定义一个新类型来描述模型DefinedModel
的定义。另外接收一个通用的T
以从接口定义的数据库中获取响应。
第 2 步:
创建解析connection.define
的模型实例返回我们的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);
【讨论】:
【参考方案3】:在@Shadrech 评论之后,我有一个替代方案(不那么老套和抽象)。
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;
使用您的模型:
sequelize.query("SELECT ...").then((user: UserInstance & UserModelInstanceMethods) =>
user.verifyPassword(req.body.password) // <= from UserModelInstanceMethods
user.getDataValue('name') // <= from UserInstance
)
【讨论】:
【参考方案4】:我见过的一个解决方案是在声明模型后强制键入。所以
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;
【讨论】:
以上是关于使用 Typescript 在 Sequelize 模型中创建实例方法的主要内容,如果未能解决你的问题,请参考以下文章
使用 TypeScript 在 Node.js 中进行 Sequelize - 没有“new”就无法调用类构造函数模型
TypeScript - 带有 Sequelize 的存储库模式
sequelize/sequelize-typescript - 带有 HasMany 的 findAll 返回一个对象而不是数组