是否可以在TypeScript中的抽象类方法类型中引用实现类型?

yrefmtwq  于 2023-06-07  发布在  TypeScript
关注(0)|答案(1)|浏览(209)

我有一个抽象类Thingydoo和一个扩展它的类Thingymabob。我在Thingydoo上有一些静态方法,我想创建一些Thingymabob中存在的类型。我尝试这样做的根本原因是为了给不同类型的Thingydoo给予一个API,以便以编程方式为每种类型创建不同的选项。

interface MyStuff {
    [name: string]: {someParam: string}
};

type Something<T> = {
    thing: T;
}

abstract class Thingydoo {
    //                  here - on this line - TypeScript uses "this" to mean Thingydoo, not the Thingymabob that comes from it
    static testWithTypeParam(thing: ReturnType<typeof this.differentFunc>): Something<ReturnType<typeof this.differentFunc>> {
        // here, in this line, TypeScript _knows_ that this.name is Thingymabob, not Thingydoo.
        console.log("hi! this is a " + this.name);
        return {thing: thing};
    }

    static differentFunc() : MyStuff {
        return {};
    }
}

class Thingymabob extends Thingydoo {
    static differentFunc(): {beans: {someParam: string}} {
        return {beans: {someParam: "test"}};
    }

    static feedMeBeans(beansContainer: {beans: {someParam: string}}) {
        console.log(beansContainer.beans.someParam);
    }
}

let something = Thingymabob.testWithTypeParam({beans: {someParam: "test3"}});
// so this, despite making sense and compiling to JS fine + running fine in JS, issues a compiler error in tsc, because it doesn't realise the beans type is MyStuff compatible.
Thingymabob.feedMeBeans(something.thing);

在这个例子中,我希望testWithTypeParam返回一个{beans: {someParam: string}}类型的对象,以便与Thingymabob上的feedMeBeans方法兼容。但这似乎根本不可能:我找不到做这件事的方法。我尝试过使用多态的this类型,如示例代码所示,但在该路由上没有取得任何进展:这似乎完全没有区别(Playground)。
我错过了什么明显的东西吗?

enyaitl3

enyaitl31#

TypeScript不直接支持static成员的多态this类型。在microsoft/TypeScript#5863上有一个长期的开放特性请求,但它还没有实现(还没有?)除非发生这种情况,否则你就得努力解决它。
对于static methods,一个常见的解决方法是将T类型中的方法generic约束为您希望this成为的某个超类型(如果您的类名是Foo,则可能是typeof Foo,但它不必是),然后为该方法提供T类型的this参数。然后(这可能是您遗漏的部分)使用类型T代替类型this。所以你不用

class Foo { 
  ⋯
  static method(arg: A<this>): R<this> {⋯} 
  ⋯
}

你会

class Foo { 
  ⋯
  static method<T extends typeof Foo>(this: T, arg: F<T>): R<T> {⋯} 
  ⋯
}

对于类似于

abstract class Thingydoo {
    
    static testWithTypeParam<T extends typeof Thingydoo>(
        this: T, thing: ReturnType<T["differentFunc"]>
    ): Something<ReturnType<T["differentFunc"]>> {
        console.log("hi! this is a " + this.name);
        return { thing: thing };
    }

    static differentFunc(): MyStuff {
        return {};
    }
}

请注意,T是一个类型,而不是一个值,所以不能写typeof T.differentFunc。相反,您需要“T的属性类型,其键是"differentFunc"”,您可以通过indexed access typeT["differentFunc"]获得。
现在它可以按预期工作:

let something = Thingymabob.testWithTypeParam({ beans: { someParam: "test3" } });
// let something: Something<{ beans: { someParam: string; }; }>
Thingymabob.feedMeBeans(something.thing); // okay

Playground链接到代码

相关问题