【问题标题】:Typescript: return type of the child class from methodTypescript:从方法返回子类的类型
【发布时间】:2021-04-05 00:48:20
【问题描述】:

我有一个模型类和 2 个从模型扩展而来的子类,如下所示:

基础模型模块:

/**
 * 
 *  Provider: Model
 *  @module providers/core/utils/model
 *  
 *  @description provides an easy to handle sequelize model
 * 
 */
import { Model as SequelizeModel, InitOptions, ModelAttributes } from 'sequelize'
import sequelize from '@core/libs/sequelize'


export default
    /**
     * 
     *  @class Model @extends SequelizeModel
     *  @description provides a more simply layer to use models
     * 
     */
    class Model extends SequelizeModel {

        /**
         *  @default options
         */
        public static options: InitOptions = {
            sequelize,
            timestamps: true
        }

        /**
         *  @custom options
         */
        public static $options: object = {}

        /**
         *  @model definition
         */
        public static attributes: ModelAttributes = {}


        /**
         *  @init the model
         */
        public static init (): SequelizeModel { // instead of return SequelizeModel I need to return the class who calls itself... (this)

            // here just call init and not return yet
            return super.init.call( this, this.attributes, {
                ...this.options,
                ...this.$options
            })

            // and add here return this
        }

    }
;

从模型扩展的类:

/**
 *
 *  Model: User
 *  @module app/models/user
 * 
 *  @description basic user model
 * 
 */
import { Model } from '@bananasplit-js' // this is the Model above
import { DataTypes, ModelAttributes } from 'sequelize'


class User extends Model {

    /**
     *  @fields
     */
    public id!: number
    public name!: string
    public lastname!: string
    public email!: string
    public password!: string


    /**
     *  @model
     */
    public static attributes: ModelAttributes = {

        id: {
            type: DataTypes.INTEGER.UNSIGNED,
            autoIncrement: true,
            
            primaryKey: true
        },

        name: {
            type: DataTypes.STRING,
            allowNull: false
        },

        lastname: {
            type: DataTypes.STRING,
            allowNull: false
        },

        email: {
            type: DataTypes.STRING,
            allowNull: false
        },

        password: {
            type: DataTypes.STRING,
            allowNull: false
        }
        
    }


    /**
     *  @options
     */
    public static $options: object = {
        timestamps: true
    }

}


export default User.init() // need to return User class (for static methods usage)
// the same with product model
class Product extends Model {
  ... // same as User model but for products
}

Product.init() // need to return Product class

我想避免两行:

User.init()
export default User

把它变成一个:

export default User.init()

所以,我需要在从 Model 扩展的类中调用 User.init()Product.init()AnyClass.init() 时,返回的类型是调用 init 方法的类的类型:User|Product|AnyClass

值得一提的是,我的 Model 类也是从 Sequelize Model 类扩展而来的,它使用静态方法。

所以在我的控制器中,我使用这样的用户模型:

const users = User.findAll() // a list of all users

你可以让工作空间从 gitpod 运行:

https://gitpod.io/#snapshot/0a676aff-fdf2-4585-b349-53516d0c677e

运行服务器:yarn dev(url 路径:/test-seeder)这是可选的

运行测试(如果一切正常应该通过):yarn test setup

我需要做这些改变

【问题讨论】:

    标签: typescript sequelize.js


    【解决方案1】:

    还有一个问题尚未解决:https://github.com/microsoft/TypeScript/issues/5863

    【讨论】:

      【解决方案2】:

      解决这个问题的技巧是设置神奇的this关键字作为静态方法的第一个参数。

      上面的sn-p有2个用例:

      • 据我所知,SequelizeModel 是一个抽象类,因此当您扩展它时,您必须返回正确类型的init 静态方法,它是类的实例而不是类本身。所以代码看起来像这样:
      class Model extends SequelizeModel {
       
        public static init<M extends Model>(this: { new(): M }) { // `M` is an instance
          return new this();
        }
      }
      
      class User extends Model {
        baz: number = 1;
      }
      
      User.init().baz // result would be an instance of `User`
      
      • 如果您希望返回不同的静态方法返回类本身而不是它的实例:
      class Model extends SequelizeModel {
        
        public static yourMethod<M>(this: M) { // `M` is now just a class
          return this;
        }
      }
      
      class User extends Model {
        static baz: number = 1;
      }
      
      User.init().baz // result would be class of `User`
      

      编辑真实代码

      class Model extends SequelizeModel {
        /**
         *  @default options
         */
        public static options: InitOptions = {
          sequelize,
          timestamps: true,
        };
      
        /**
         *  @custom options
         */
        public static $options: object = {};
      
        /**
         *  @model definition
         */
        public static attributes: ModelAttributes = {};
      
        /**
         *  @init the model
         */
        public static init<M extends Model>(this: { new(): M }) {
          return super.init.call(this, Model.attributes, {
            ...Model.options,
            ...Model.$options,
          });
        }
      }
      

      【讨论】:

      • 我不需要返回一个新的this 实例。当我执行示例 2 时,我的 super.init.call(this, this.attributes, this.options) 出现错误:Argument of type 'T' is not assignable to parameter of type 'ModelStatic&lt;Model&lt;any, any&gt;&gt;'.
      • 目前init 方法将this 定义为this: ModelStatic&lt;M&gt; (type ModelStatic&lt;M extends Model&gt; = { new(): M }),与M 不兼容。我建议现在将this 转换为any 作为解决方法:super.init.call(this as any, this.attributes, this.options)
      • 我无法工作...我将用整个代码版本更新帖子
      • 我给你一个更新的代码给你。不知道你为什么通过 this 关键字访问静态成员,这应该是类名
      • 非常感谢,但当我执行 User.findAll() 时,我得到:Property 'findAll' is a static member of type 'Model&lt;any, any&gt;'。在 init 方法中,我只是试图将子属性以及默认和自定义选项的合并传递给超级初始化函数
      猜你喜欢
      • 2021-07-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多