[英]TypeScript inheritance static methods cast
我有一个包含两个类 BaseModel 和 HotelModel 的 TypeScript 项目。 HotelModel 扩展了 BaseModel 类,该类提供了一些静态方法,如 findById、all 等。
export default class BaseModel {
private collection:string
_id:string | undefined
constructor (collection:string) {
this.collection = collection
}
static getCollectionName () {
return this.prototype.constructor.name.toString().toLowerCase() + 's'
}
static async findById (id:string) {
const connection = await getConnection()
const hotel = await connection.collection(this.getCollectionName())
.findOne({
_id: new mongodb.ObjectId(id)
})
if (!hotel) {
throw new ResourceNotFound('Hotel not found with the given id' + id)
}
return hotel
}
}
这是 HotelClass
import BaseModel from './baseModel'
import IHotel from '../interfaces/IHotel'
import ValidationException from '../../exceptions/ValidationException'
export default class Hotel extends BaseModel {
name:string
status:string
metadata:object
constructor (hotel:IHotel) {
super('hotels')
this.name = hotel.name
this.status = hotel.status
this.metadata = hotel.metadata
}
validate () {
if (!this.name || this.name === '') {
throw new ValidationException('Name field is required')
}
}
}
现在,当我调用 HotelModel.findById(1) 时,我想收到成员类 (HotelModel) 的一个实例,这可能吗? 我怎样才能做到这一点?
- - - 更新 - - -
根据建议,这就是我得到的
export default class Service<T> {
private collection:string
constructor (collection:string) {
this.collection = collection
}
async findById (id:string) {
const connection = await getConnection()
const model = await connection.collection(this.collection)
.findOne({
_id: new mongodb.ObjectId(id)
}) as T
if (!model) {
throw new ResourceNotFound('Model not found with the given id' + id)
}
return model
}
}
然后我有一个 HotelService 类,它扩展了通用类并继承了所有方法
export default class HotelService extends Service<HotelModel> {
public constructor () {
super('hotels')
}
}
------更新 2------
好吧,花了很多时间,但我找到了一个“优雅”(至少对我而言)的解决方案来解决问题
class QueryBuilder {
private modelType: typeof BaseModel;
constructor (modelType: typeof BaseModel) {
this.modelType = modelType
}
data:Array<any> = [
{ id: '1', name: 'Jane' },
{ id: '2', name: 'John' },
{ id: '3', name: 'Mark' }
]
findById (id:string) {
// fake database call
const data = this.data.find(r => r.id === id)
// "cast" the database object to the required type
let model:any = new this.modelType()
model.fill(data)
return model
}
}
class BaseModel {
private id:string | undefined
constructor () {}
static findById () {
return new QueryBuilder(this)
.findById('1')
}
public save () {
console.log('calling save')
this.id = '123456'
}
public fill (data:any) {
}
}
class HotelModel extends BaseModel {
public name:string | undefined
constructor (
name:string
) {
super()
}
}
let h:HotelModel = HotelModel.findById()
h.name = 'test name'
h.save()
console.log(h)
console.log(h instanceof HotelModel)
谢谢
我相信这就是你所追求的
export default class BaseModel {
collection: string
_id: string | undefined
constructor(collection: string) {
this.collection = collection;
}
static get collectionName() {
return this.name.toLowerCase() + 's';
}
static async findById<T extends BaseModel>(
this: (new (...args: any[]) => T) & Pick<typeof BaseModel, keyof typeof BaseModel>,
id: string
): Promise<T> {
const connection = await getConnection();
const model = await connection.collection(this.collectionName)
.findOne({
_id: new mongodb.ObjectId(id)
});
if (!model) {
throw new ResourceNotFound(`${this.collectionName} not found with the given id ${id}`);
}
return model as T;
}
}
export default class Hotel extends BaseModel { ... }
const hotel = await Hotel.findOneBy('1');
console.log(hotel.name);
console.log(hotel.status);
那么,这里发生了什么?
我们正在使用 TypeScript 的功能来指定函数和方法隐式接收的this
值的类型。
由于我们在static
方法中,因此this
类型指的是类本身的类型。 该类型是我们可以用new
调用的东西,也就是说它是一个构造函数。
但是,我们想要捕获派生类的实际类型。 为此,我们声明了一个泛型类型T
,它表示派生类在我们使用new
调用它时返回的任何内容。 然后我们声明this
是一个创建T
的构造函数。 然而,这样做时我们失去了对基类静态成员的访问权限,我们必须用交集重新添加它们。
最后,当我们调用Hotel.findById
,打字稿推断T
从typeof Hotel
,因为typeof Hotel
是值的类型findById
被称为上。
注意:通常, findById
的this
类型写起来更简单,即(new (...args: any[]) => T) & typeof BaseModel
但在这种情况下,您的派生类Hotel
有一个不兼容的构造函数参数列表。 我使用Pick<typeof BaseModel, keyof typeof BaseModel>
作为获取包含typeof BaseModel
所有成员(调用和构造签名除外)的类型的快速而肮脏的方法。
重载酒店静态函数
static async findById (id:string) {
const data = await BaseModel.findById(id)
return new Hotel(data)
}
我不习惯打字稿,所以也许有人可以帮助我,但在您更新之后,我认为您需要传递实际的构造函数值而不仅仅是一个类型
这是一个例子
class Service {
private collection: string
private Model: any
constructor (Model: any, collection: string) {
this.collection = collection
this.Model = Model
}
findById (id:string) {
console.log(this.collection)
return new this.Model(id)
}
}
class HotelModel {
public id: string
constructor (id: string) {
this.id = id
}
test () {
return '1'
}
}
class HotelService extends Service {
constructor () {
super(HotelModel, 'hotels')
}
}
const hotelService = new HotelService()
const hotel = hotelService.findById('1')
console.log(hotel.test())
我在 super 内部传递实际类并在 getFindId() 中使用它来返回此类的实例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.