使用TS+Sequelize实现更简洁的CRUD (3)

但是如果你像上边那样使用的话,TS会提示错误的:[ts] 类型“Animal”上不存在属性“leg”。。
哈哈,这又是为什么呢?细心的同学可能会发现,getList的返回值是一个Animal[]类型的,所以上边并没有leg属性,Bird的两个属性也是如此。

所以我们需要教TS认识我们的数据结构,这样就需要针对Animal的定义进行修改了,用到了 范型
我们通过在函数上边添加一个范型的定义,并且添加限制保证传入的范型类型一定是继承自Animal的,在返回值转换其类型为T,就可以实现功能了。

class Animal { static async getList<T extends Animal>() { const results = await this.findAll({ raw: true, }) return results as T[] } } const dogList = await Dog.getList<Dog>() // 或者不作任何修改,直接在外边手动as也可以实现类似的效果 // 但是这样还是不太灵活,因为你要预先知道返回值的具体类型结构,将预期类型传递给函数,由函数去组装返回的类型还是比较推荐的 const dogList = await Dog.getList() as Dog[] console.log(dogList[0].leg) // success

这时再使用leg属性就不会出错了,如果要使用范型,一定要记住添加extends Animal的约束,不然TS会认为这里可以传入任意类型,那么很难保证可以正确的兼容Animal,但是继承自Animal的一定是可以兼容的。

当然如果连这里的范型或者as也不想写的话,还可以在子类中针对父类方法进行重写。
并不需要完整的实现逻辑,只需要获取返回值,然后修改为我们想要的类型即可:

class Dog extends Animal { static async getList() { // 调用父类方法,然后将返回值指定为某个类型 const results = await super.getList() return results as Dog[] } } // 这样就可以直接使用方法,而不用担心返回值类型了 const dogList = await Dog.getList() console.log(dogList[0].leg) // success 小结

本文只是一个引子,一些简单的示例,只为体现出三者(SQL、Sequelize和Sequelize-typescript)之间的区别,Sequelize中有更多高阶的操作,类似映射关系之类的,这些在Sequelize-typescript中都有对应的体现,而且因为使用了装饰器,实现这些功能所需的代码会减少很多,看起来也会更清晰。

当然了,ORM这种东西也不是说要一股脑的上,如果是初学者,从个人层面上我不建议使用,因为这样会少了一个接触SQL的机会
如果项目结构也不是很复杂,或者可预期的未来也不会太复杂,那么使用ORM也没有什么意义,还让项目结构变得复杂起来
以及,一定程度上来说,通用就意味着妥协,为了保证多个数据库之间的效果都一致,可能会抛弃一些数据库独有的特性,如果明确的需要使用这些特性,那么ORM也不会太适合
选择最合适的,要知道使用某样东西的意义

最终的一个示例放在了GitHub上:notebook | typescript/sequelize

参考资料:

mysql | npm

sequelize

sequelize-typescript | npm

waht are the advantages of using an orm

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxddd.html