有一个github issue上Polymorphic this在静态方法中还有一个相关的问题here 。这些正在讨论如何解决/解决静态类方法的问题。但是,在静态类成员的情况下,我找不到任何引用来解决这个问题。 我们有一个模型类,该类有一些包含映射的静态成员,其中键是模型字段。我们在继承的类中使用属性装饰器来标记模型字段(继承的类仅包含字段定义)。装饰器还将字段添加到基类中定义的静态映射中。请参阅下面和 playgound 中的代码
function field(): any {
return function (target: Base, propertyKey: ModelFields<Base>, _descriptor: PropertyDescriptor) {
if (!target.constructor.fields) {
target.constructor.fields = {};
}
target.constructor.fields[String(propertyKey)] = String(`s_${propertyKey}`);
};
}
class Base {
['constructor']: typeof Base;
static fields: Record<string, string>;
// Actually the above is supposed to be Record<keyof ModelFields<this>, string> but 'this' is not allowed here
}
class A extends Base {
@field()
public propA: string = 'A';
}
class B extends Base {
@field()
public propB: string = 'B';
}
type ModelFields<T extends Base> = Required<Omit<T, keyof Base>>
console.log(B.fields) // B.fields type is not correct here
目前静态fields
定义为Record<string, string>
但它并没有告诉它上面存在什么键,尽管我们知道有效的键是 keyof ModelFields<this>
。但显然this
那里是不允许的。
是否有任何解决方案可以输入 fields
对吗?
最佳答案
您可以采取一些措施来更接近您想要的效果。
您发现的第一个问题是静态成员中缺乏多态 this
。我们可以用方法来替换该字段。在方法上,我们可以使用泛型类型来捕获调用站点类型:
class Base {
['constructor']: typeof Base;
static fields: Record<any, any>;
static getFields<TThis extends typeof Base>(this: TThis) {
type I = InstanceType<TThis>;
return this.fields as {
[P in keyof I] : string
}
}
}
另一个问题是无法根据装饰器过滤成员(这些成员未在类型系统中表示)。您可以做的是将品牌添加到具有装饰器的所有字段的类型中,并过滤该品牌的字段:
class Base {
['constructor']: typeof Base;
static fields: Record<any, any>;
static getFields<TThis extends typeof Base>(this: TThis) {
type I = InstanceType<TThis>;
return this.fields as {
[P in keyof I as I[P] extends FieldBrand<unknown> ? P : never] : string
}
}
}
declare const fieldBrand: unique symbol;
type FieldBrand<T> =T & { [fieldBrand]?: never }
type UnBrandField<T> = T extends FieldBrand<infer U> ? U: never;
class A extends Base {
@field()
public propA: FieldBrand<string> = 'A';
}
关于Typescript:自引用静态类成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65942235/