如何处理静态泛型属性继承在Typescript?



我正在尝试为Firestore对象建模一个基于浅类的接口,以封装自定义对象转换器。考虑两个具有自定义转换器和集合名称的类,它们具有一个共同的docRef函数,它们可能具有除了单个字符串之外的其他属性:

class Foo {
constructor(public fooVal: string) {}
// Table / collection specific functions bound to this model
static #firestoreConverter: FirestoreDataConverter<Foo> =
{
toFirestore(modelObject) {
return {
fooVal: modelObject.fooVal,
};
},
fromFirestore(snapshot, options?) {
const data = snapshot.data(options);
return new Foo(data.fooVal);
},
};
static #collectionName = "Foo";
// --- Refactor from here on ---
static #collection = collection(db, this.#collectionName).withConverter(
this.#firestoreConverter
);
static docRef(id: string) {
return doc(this.#collection, id);
}
}
class Bar {
constructor(public barVal: string) {}
// Table / collection specific functions bound to this model
static #firestoreConverter: FirestoreDataConverter<Bar> =
{
toFirestore(modelObject) {
return {
barVal: modelObject.barVal,
};
},
fromFirestore(snapshot, options?) {
const data = snapshot.data(options);
return new Bar(data.barVal);
},
};
static #collectionName = "Bar";
static #collection = collection(db, this.#collectionName).withConverter(
this.#firestoreConverter
);
static docRef(id: string) {
return doc(this.#collection, id);
}
}

我无法解析类型依赖的转换器属性(firestoreCoverter返回类型取决于特定的类)。基于继承的解决方案会陷入困境,因为返回类型是子类型而不是父类型,但在创建子类型之前我们不知道子类型是什么。例如:

abstract class BaseModel {
static collectionName: string;
// Here, the type of firestoreConverter will change in the child class
static firestoreConverter: FirestoreDataConverter<BaseModel>;
static docRef(id: string) {
const col = collection(db, this.collectionName).withConverter(
this.firestoreConverter
);
return doc(col, id);
}
}
// If you inherit, e.g. Foo extends BaseModel
// We get Foo.docRef type to be FirestoreDataConverter<BaseModel>
// even though Foo.converter is of type FirestoreDataConverter<Foo>

Mixin也与converter属性的返回类型作斗争。是否存在一种类型安全且优雅的方法来重构这两个类?

一个解决方案是将mixin封装在一个泛型函数中,以处理文档中建议的静态属性。在这种情况下,父类是特定于子类的类型:

function baseModel<T>() {
return class FirestoreModel {
static collectionName: string;
// Here, we defer the type of the static property
static firestoreConverter: FirestoreDataConverter<T>;
static docRef(id: string) {
const col = collection(db, this.collectionName).withConverter(
this.firestoreConverter
);
return doc(col, id);
}
};
}
class Foo extends baseModel<Foo>() { /* ... */ }
// Looking at Foo.docRef now yields FirestoreDataConverter<Foo>
class Bar extends baseModel<Bar>() { /* ... */ }
// Bar.docRef now yields FirestoreDataConverter<Bar>

它可以使用私有静态属性,但Typescript可能会警告Foo和Bar没有使用私有静态属性,否则会推断出正确的类型。

最新更新