如何告诉typescript我的函数返回一个由特定类扩展的类



考虑以下类:

class A {
constructor($elem: JQuery<HTMLElement>) {
$elem.data('plugin', this);
}
inheritedMethod() {
...
}
}
class B extends A {
constructor($elem: JQuery<HTMLElement>) {
super($elem);
}
specificMethodB() {
...
}
}
class C extends A {
constructor($elem: JQuery<HTMLElement>) {
super($elem);
}
}
getAishClass($elem: JQuery<HTMLElement>): A {
return $elem.data('plugin');
// this function will return either a class B or a class C
}

const $bDiv = jQuery("<div></div>");
const $cDiv = jQuery("<div></div>");
new B($bDiv);
new C($cDiv);
const thisIsAC = getAishClass($cDiv);
thisIsAC.specificMethodC(); // this tells me that I have an error, A does not implement this method

我可以告诉getAishClass它返回一种类型的any,但有没有一种方法,我可以明确指定,函数返回的值将是一个类,肯定扩展了class A

基本上,在上面的例子中,I(程序员(和程序";可以";知道CCD_ 4将返回CCD_。但考虑到上面的例子,在某些情况下,我将在jQuery元素上调用getAishClass方法,我不知道它实现了什么类型的A。在这些情况下,我希望至少对A类中的方法进行类型提示,但在所有其他情况下,不希望ts抱怨函数没有实现。

`
class A {
someMethod() {}
}
class B extends A {
someMethodB() {}
}
class C extends A {
someMethodC() {}
}
class D {}
function myfunc(): A {
return new B();
}
const btype = <B>myfunc();
btype.someMethodB(); // no problem to IDE here
function myfunc2(): A {
// return new A(); // this is fine.
// return new B(); // also fine
// return new C(); // also fine
// return new D(); // this is not fine, ts will complain. 
}

您要做的是…当您调用new B(elem)时,您希望编译器对elem的类型做些什么,以记住它是用new B()调用的。这不是TypeScript的正常工作方式。

您可以类型的排序对断言函数执行类似操作,这样当您以elem作为参数调用函数时,CCD_12的类型将缩小。不幸的是,您不能使用构造函数,只能使用函数或方法。这意味着我们需要制作自己的类似new的函数,该函数采用构造函数和JQuery元素,并缩小元素类型:

function magicNew<T>(ctor: new (e: JQuery<HTMLElement>) => T, elem: JQuery<HTMLElement>):
asserts elem is JQuery<HTMLElement> & { __plugindata__: T } {
new ctor(elem);
}

因此,您不调用new B(elem),而是调用magicNew(B, elem)

哦,即便如此,我还是在骗编译器。我假设当您调用magicNew(B, $bDiv)时,实现正在向$bDiv添加一个名为B类型的__plugindata__的属性。这当然不会在运行时发生;它更像是使$bDiv.data('plugin')的返回类型等于B,但这不是我们稍后在getAishClass中可以使用的。谎言允许我们强迫编译器记住一些";额外的";关于CCD_ 23。

现在getAishClass()看起来是这样的:

function getAishClass<T extends JQuery<HTMLElement>>($elem: T): T extends {__plugindata__: infer U} ? U : A {
return $elem.data('plugin');
}

这里我们看的是传入的$elem。如果编译器认为它有一个__plugindata__属性,它会给我们该属性类型;否则它只给我们CCD_ 28。因此,如果编译器认为幻影属性存在,我们就会提取它。

我认为,这会给你带来你所要求的行为:

const $bDiv = jQuery("<div></div>");
const $cDiv = jQuery("<div></div>");
magicNew(B, $bDiv); // do this instead of new B($bDiv)
magicNew(C, $cDiv); // do this instead of new C($cDiv)
const thisIsAC = getAishClass($cDiv);
thisIsAC.specificMethodC(); // no error

耶!

值得吗?它有许多移动的碎片,它们很奇怪,可能很脆弱。如果这是用于某些生产代码,我会远离它。如果是用于个人用途,我可能会使用它。无论如何,我希望它能让你了解TypeScript能力的局限性和优势。

祝你好运!

游乐场链接

最新更新