【问题标题】:Creating instance methods in a Sequelize Model using Typescript使用 Typescript 在 Sequelize 模型中创建实例方法
【发布时间】: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 会抱怨。如何添加到打字?

【问题讨论】:

    标签: node.js typescript model sequelize.js


    【解决方案1】:

    根据主要的Sequelize TypeScript Doc,我认为最好的实现方式是在模型创建界面使用DataTypes.VIRTUAL,跳过带有TypeScript Omit utility的属性。

    重要!记住Issue#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://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",
      }
    );
    
    

    【讨论】:

      【解决方案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;
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2020-02-22
            • 2013-10-26
            • 1970-01-01
            • 2019-02-04
            • 1970-01-01
            • 2020-04-03
            • 2019-01-23
            • 1970-01-01
            相关资源
            最近更新 更多