简体   繁体   English

扩展子类的 object 成员

[英]Extending object member of subclass

I'm building Page Objects for my automated tests, and I'm doing something like this:我正在为我的自动化测试构建页面对象,我正在做这样的事情:

abstract class Page  {
  selectors: {
    // Here are selectors which are common to all my pages
    genericSelector: "...",
    commonContainer: {
      aButton: "..."
    }
  }

  // methods that are common to all my pages
}
class MyPage extends Page {
  
  // Here I want my page specific selectors
  // *plus* my generic selectors 
  selectors: {
    ...this.selectors,
    mySpecificSelector: "..."
  }
}

The above code works fine at run-time: I can correctly use all my selectors in my code.上面的代码在运行时运行良好:我可以在我的代码中正确使用我的所有选择器。 However, Typescript complains on MyPage.ts that:但是,Typescript 在MyPage.ts上抱怨说:

Property 'selectors' in type 'MyPage' is not assignable to the same property in base type 'Page.

Basically because TS doesn't know that I'm spreading the base class prop into the subclass.基本上是因为 TS 不知道我将基础 class 道具传播到子类中。

Now, the following is what I tried:现在,以下是我尝试过的:

One additional note附加说明

My type for the selector property looks like this:我的选择器属性类型如下所示:

type Selector = string | SelectorFactory | { selector: string, type: "css" | "xpath" }
type SelectorFactory = (...args: any[]) => string;
type SelectorTree = { [k: string]: SelectorTree | Selector }

Convenient TS Playground link 方便的 TS Playground 链接

What I tried我试过的

First attempt第一次尝试

class MyPage extends Page {
  selectors: {
    ...this.selectors as InstanceType<typeof Page>["selectors"]
  }
}

This, then complains:然后抱怨:

'selectors' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer.

So I have to do:所以我必须这样做:

  selectors: SelectorTree = // ...

Then I'm back to square one, as it complains that it is missing some properties from the base class.然后我回到第一方,因为它抱怨它缺少基本 class 的一些属性。 If I add : SelectorTree to my base class, it then doesn't complain, but I don't get proper intellisense and I even get errors (for example if I try to call a function from my selectors properties, I can't).如果我将: SelectorTree添加到我的基础 class,它不会抱怨,但我没有得到正确的智能感知,我什至得到错误(例如,如果我尝试从我的选择器属性调用 function,我不能) .

Second attempt第二次尝试

I could, I guess, have my selectors member of the base Page class be static.我想,我可以让基本Page class 的selectors成员为 static。 Then I could do:然后我可以这样做:

  selectors: {
    ...Page.selectors,
    mySpecificSelector: "..."
  }

This works, but, well, I now actually have an extra static member that I did not want to have.这可行,但是,我现在实际上有一个我不想拥有的额外 static 成员。

Third attempt, aka “I give up”第三次尝试,又名“我放弃”

I could separate the members and have something like baseSelectors on my Page class and then I could either use them as they are within the sub-class, or spread them into my selectors object.我可以将成员分开并在我的Page class 上使用类似baseSelectors的内容,然后我可以在子类中使用它们,或者将它们传播到我的selectors object 中。

But this feels like a defeat.但这感觉像是一场失败。

This unfortunately seems to be a design limitation in TypeScript, according to microsoft/TypeScript#33899 :不幸的是,这似乎是 TypeScript 中的设计限制,根据microsoft/TypeScript#33899

Due to architectural constraints around how trees are marked as checked, this has to be treated as a circular reference despite the type assertion.由于围绕如何将树标记为已检查的架构限制,尽管有类型断言,但必须将其视为循环引用。

Apparently any reference to this.selectors , no matter how indirect, seems to cause a circularity error.显然,对this.selectors的任何引用,无论多么间接,似乎都会导致循环错误。 So far, the only workaround I've found that gives you the right types without having to refactor the emitted code is to widen this to Page (note: InstanceType<typeof Page> is the same as Page ) to get a non- any type for this.selectors , and then just brute-force suppressing the circularity warning with //@ts-ignore :到目前为止,我发现的唯一解决方法可以为您提供正确的类型而无需重构发出的代码是将其扩大this Page (注意: InstanceType<typeof Page>Page相同)以获得非any类型对于this.selectors ,然后使用//@ts-ignore蛮力抑制循环警告:

class SubPage extends Page {
  //@ts-ignore circularity warning
  selectors = { ...(this as Page).selectors, foo: "bar", open: () => "" }
}

You can verify that instances of SubPage behave as expected:您可以验证SubPage的实例是否按预期运行:

const p = new SubPage();
p.selectors;
// SubPage.selectors: {
//   foo: string;
//   open: () => string;
//   test: string;
// }
p.selectors.open().toUpperCase(); // okay

That works.这样可行。 But...但...

⚠ ALERT ⚠ I feel very uneasy whenever someone suggests //@ts-ignore because if the suppressed error has any other unpleasant effects on your code, they will still exist. ⚠ ALERT ⚠每当有人建议//@ts-ignore时,我都会感到非常不安,因为如果被抑制的错误对您的代码有任何其他不愉快的影响,它们仍然存在。 I didn't see any such effects with the example code above, but I can't be certain they're not there.在上面的示例代码中我没有看到任何这样的效果,但我不能确定它们不存在。 If your automobile mechanic addressed a lit "check engine" indicator light by covering the indicator light so that you can't see it anymore, you'd maybe want to start looking around to find a different mechanic, if only for a second opinion.如果您的汽车修理工通过遮盖指示灯来解决点亮的“检查发动机”指示灯,这样您就再也看不到它了,那么您可能想开始四处寻找不同的修理工,即使只是为了第二个意见。

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM