There are some things you can do to get closer to your desired effect.
The first issue as you discovered is the lack of polymorphic this
in static members. We can replace the field with a method. On a method we can use the generic type to capture teh call site type:
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
}
}
}
Playground Link
The other problem is that there is no way to filter members based on a decorator (these are not represented in the type system). What you could do is add a brand to the type all fields that have the decorator and filter the fields on that brand:
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';
}
Playground Link
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…