Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
421 views
in Technique[技术] by (71.8m points)

Typescript: self referencing static class members

There is a github issue on Polymorphic this in static methods and also a related question here. These are discussing how to solve/work around the problem in case of static class methods. However I could not find any reference to solve this problem in case of static class members. We have a model class and this class has a few static members containing maps where the keys are the model fields. We are using property decorators in the inherited classes to tag model fields (the inherited classes containing only field definitions). The decorator is also adding the fields to the static maps defined in the base class. See the code below and in 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

currently the static fields is defined as Record<string, string> but it doesn't tell what keys exists on it although we know that the valid keys are keyof ModelFields<this>. But obviously this is not allowed there.

Is there any solution to get the typing of fields right?

question from:https://stackoverflow.com/questions/65942235/typescript-self-referencing-static-class-members

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...