TypeScript继承静态方法强制转换



我有一个TypeScript项目,它有两个类BaseModel和HotelModel。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是一个创建Ts的构造函数。然而,在这样做的过程中,我们失去了对基类静态成员的访问权限,我们必须通过交集将它们添加回。

最后,当我们调用Hotel.findById时,TypeScript从typeof Hotel推断出T,因为typeof HotelfindById被调用的值的类型。

注意:通常,findByIdthis类型更容易编写,即(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((中使用它来返回这个类的实例。

最新更新