[英]TypeScript inheritance static methods cast
I have a TypeScript project with two classes BaseModel and HotelModel.我有一个包含两个类 BaseModel 和 HotelModel 的 TypeScript 项目。 The HotelModel extends the BaseModel class that provides some static methods like findById, all, etc..
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
}
}
and this is the HotelClass这是 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')
}
}
}
Now when i call HotelModel.findById(1) i would like to receive back an istance of the member class (HotelModel) is this possible?现在,当我调用 HotelModel.findById(1) 时,我想收到成员类 (HotelModel) 的一个实例,这可能吗? How can i achieve that?
我怎样才能做到这一点?
------UPDATE------ - - - 更新 - - -
based on suggestion this is what i got根据建议,这就是我得到的
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
}
}
then i have a HotelService class that extends the generic one and inherits all the methods然后我有一个 HotelService 类,它扩展了通用类并继承了所有方法
export default class HotelService extends Service<HotelModel> {
public constructor () {
super('hotels')
}
}
------UPDATE 2------ ------更新 2------
Well, it took quite a lot of time but I found an "elegant" (at least to me) solution to solve the problem好吧,花了很多时间,但我找到了一个“优雅”(至少对我而言)的解决方案来解决问题
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)
Thank you谢谢
I believe this is what you are after我相信这就是你所追求的
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);
So, what's going on here?那么,这里发生了什么?
We are using using TypeScript's ability to specify the type of the this
value that functions and methods implicitly receive.我们正在使用 TypeScript 的功能来指定函数和方法隐式接收的
this
值的类型。
Since we are in a static
method, the this
type refers to the type of the class itself.由于我们在
static
方法中,因此this
类型指的是类本身的类型。 That type is something we can call with new
which is to say it is a constructor.该类型是我们可以用
new
调用的东西,也就是说它是一个构造函数。
However, we want to capture the actual type of the derived class.但是,我们想要捕获派生类的实际类型。 To this end, we declare a generic type,
T
, that represents whatever a derived class returns when we call it with new
.为此,我们声明了一个泛型类型
T
,它表示派生类在我们使用new
调用它时返回的任何内容。 Then we state that this
is a constructor that creates T
s.然后我们声明
this
是一个创建T
的构造函数。 We've lost access to the static members of the base class in doing so, however, and we have to add them back in with an intersection.然而,这样做时我们失去了对基类静态成员的访问权限,我们必须用交集重新添加它们。
Finally, when we call Hotel.findById
, TypeScript infers T
from typeof Hotel
because typeof Hotel
is the type of the value findById
is being called on.最后,当我们调用
Hotel.findById
,打字稿推断T
从typeof Hotel
,因为typeof Hotel
是值的类型findById
被称为上。
Note: Normally, the this
type for findById
would be simpler to write, ie (new (...args: any[]) => T) & typeof BaseModel
but in this case, your derived class Hotel
has a constructor with an incompatible argument list.注意:通常,
findById
的this
类型写起来更简单,即(new (...args: any[]) => T) & typeof BaseModel
但在这种情况下,您的派生类Hotel
有一个不兼容的构造函数参数列表。 I used Pick<typeof BaseModel, keyof typeof BaseModel>
as a quick and dirty way to obtain a type containing all of the members of typeof BaseModel
except call and construct signatures.我使用
Pick<typeof BaseModel, keyof typeof BaseModel>
作为获取包含typeof BaseModel
所有成员(调用和构造签名除外)的类型的快速而肮脏的方法。
Overload static function of Hotel重载酒店静态函数
static async findById (id:string) {
const data = await BaseModel.findById(id)
return new Hotel(data)
}
I'm not used to typescript so maybe someone can help me but following your update I think you need to pass the actual constructor value and not just a type我不习惯打字稿,所以也许有人可以帮助我,但在您更新之后,我认为您需要传递实际的构造函数值而不仅仅是一个类型
Here is an example这是一个例子
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())
I'm passing the actual class inside super and use it inside getFindId() to return the instance of this class.我在 super 内部传递实际类并在 getFindId() 中使用它来返回此类的实例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.