node.js - Typescript kullanarak bir Sequelize Modelinde örnek yöntemler oluşturma


Sequelize Model sınıfını diğer örnek yöntemleri eklemek için genişletmek istiyorum ama TypeScript sürekli olarak "Model türünde 'prototype' özelliği mevcut değil" diye hata veriyor

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 işlev görmesini bekliyorum ama TypeScript hata veriyor. Tip eklemeyi nasıl yapabilirim?

Cevap #1

@Shadrech'in yorumunu takiben, benim bir alternatifim var (daha az geçici ve soyut).

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;
}

Modelinizi kullanarak:

sequelize.query("SELECT ...").then((user: UserInstance & UserModelInstanceMethods) => {
  user.verifyPassword(req.body.password) // <= from UserModelInstanceMethods
  user.getDataValue('name') // <= from UserInstance
})

Cevap #2

Ana Sequelize TypeScript Dokümanı'na göre, bence bunu uygulamanın en iyi yolu, DataTypes.VIRTUAL kullanmak ve modeli oluşturma arayüzünde özelliği TypeScript Omit yardımcı aracı ile atlamaktır.

Önemli! Issue#11675'i unutma!

  • https://stackoverflow.com/a/69289067/717267
  • https://github.com/sequelize/sequelize/issues/11675

Basit bir örnek:

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",
  }
);

İlgili makaleler