在TypeScript中,我如何要求子类实现静态方法?

huangapple go评论125阅读模式
英文:

In TypeScript, how can I require a subclass to implement static method?

问题

在TypeScript中,我希望给定的子类实现一个静态方法,该方法将被超类调用。该方法返回的数据实际上是类的属性,而不是实例的属性,因此从建模角度来看,将其定义为静态而不是非静态是有意义的。

在超类中调用子类定义的静态方法的当前方法如下:

const r = Object.getPrototypeOf(this).constructor.staticMethod()

第一个问题:有没有更好的方法?特别是,constructor 返回 any,这个我不太喜欢。

第二个问题:如何在编译时确保子类将实现staticMethod静态方法?我尝试过各种方法,包括使用satisfies关键字,例如:

type SuperclassStatic = { someMethod(): number }
const Subclass = class extends Superclass { ... } satisfies SuperclassStatic

但这似乎不是在TypeScript 4.9.5中的有效语法。而且,我不喜欢在每个子类声明中都添加satisfies SuperclassStatic这部分的需求。

有更好的方法吗?

英文:

In TypeScript, I want a given subclass to implement a static method which would be called by the superclass. This method returns data that is really a property of the class and not of an instance and thus makes sense as static rather than non-static from a modeling point of view.

What I'm currently doing in the superclass to call the static method defined in the subclass is this:

const r = Object.getPrototypeOf(this).constructor.staticMethod()

First question: is there a better way to do this? In particular, constructor returns any, which I never like.

Second: how can I ensure at compile-time that the subclass will implement the static method staticMethod? I've tried various approaches, including stuff with satisfies like

type SuperclassStatic = { someMethod(): number }
const Subclass = class extends Superclass { ... } satisfies SuperclassStatic

… but not only does this seem to be invalid syntax in TypeScript 4.9.5, it is also not great that I'd have to remember to add the satisfies SuperclassStatic part in every subclass declaration.

Any better way?

答案1

得分: 2

请提供您需要翻译的文本,我将为您提供翻译。

英文:

> First question: is there a better way to do this?

If this is within an instance method of the superclass (which you've confirmed in a comment it is), then while you don't have to use Object.getPrototypeOf, you are sadly stuck with this.constructor, which as you say isn't ideal as its type is just Function:

const ctor = this.constructor as /*...appropriate type, see below...*/;
// Belt-and-braces runtime check
if (!ctor.staticMethod) { // Slightly surprising TypeScript doesn't say this will never be true, but handy
    throw new Error(`Invalid subclass, has no staticMethod static method`);
}
ctor.staticMethod();

(Note: The !ctor.staticMethod check above only makes sense in conjunction with another change I make below [making the parent class use a different name for the static method than subclasses]. If you don't make that change, you might do if (ctor.staticMethod === Parent.staticMethod) { instead for the condition of that if that will throw on an invalid subclass.)

> how can I ensure at compile-time that the subclass will implement the static method staticMethod?

I don't think you can if the names are the same, but if you make the names different it's possible with the help of a do-nothing function so we can use a generic to do the check (here I've made the superclass method's name parentStaticMethod, with the subclasses expected to implement staticMethod):

type Constructor = new (...args: any[]) => any;
type RequiredSubclassMethods = { staticMethod: (...args: any[]) => any };

class Parent {
    static parentStaticMethod() {
        console.log("Parent.parentStaticMethod()");
    }

    method() {
        const ctor = this.constructor as Constructor & RequiredSubclassMethods;
        // Belt-and-braces runtime check
        if (!ctor.staticMethod) { // Slightly surprising TypeScript doesn't say this will never be true, but handy
            throw new Error(`Invalid subclass, has no staticMethod static method`);
        }
        ctor.staticMethod();
    }

    static validSub<Cls extends Constructor & RequiredSubclassMethods>(cls: Cls): Cls {
        return cls;
    }
}

// Works
const GoodSub = Parent.validSub(
    class GoodSub extends Parent {
        static staticMethod() {
            console.log("GoodSub.staticMethod()");
            super.parentStaticMethod();
        }
    }
);

// Fails, doesn't have `staticMethod`
const BadSub = Parent.validSub(
    class BadSub extends Parent {
    }
);

// Calls `GoodSub.staticMethod`()` via `Parent.prototype.method`
new GoodSub().method();
// Fails, doesn't have `staticMethod`
const BadSub = Parent.validSub(
    class BadSub extends Parent {
    //    ^^^^^^ Argument of type 'typeof BadSub' is not assignable to parameter of type 'Constructor & RequiredSubclassMethods'.
    //           Property 'staticMethod' is missing in type 'typeof BadSub' but required in type 'RequiredSubclassMethods'.(2345)
    }
);

Playground link

validSub doesn't have to be a method on Parent, but it seemed a reasonable place to put it. It could be a standalone function instead.

Of course, using that Parent.validSub is a pain and easily forgotten. 在TypeScript中,我如何要求子类实现静态方法? I thought we might be able to use implements instead of a function (similar to this answer), but if so, I can't get it to work, and in any case it would also (as you say) be a pain and easily forgotten.

huangapple
  • 本文由 发表于 2023年3月8日 17:23:04
  • 转载请务必保留本文链接:https://go.coder-hub.com/75671283.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定