TS2611:“foo”被定义为类“A”中的属性,但在“B”中作为访问器被重写

问题描述 投票:0回答:7

我在 Angular 应用程序中遇到 Typescript 编译错误。以下是示例代码和错误消息。

请您指导我抑制此错误。我认为这段代码根据 Javascript 是正确的。

- error TS2611: 'foo' is defined as a property in class 'A', but is overridden here in 'B' as an accessor.
export class A {
  foo: string;
}

export class B extends A {
  f1;
  set foo(name) {
    this.f1 = name;
  }
  get foo() {
    return this.f1;
  }
}

编者注:在评论中发现,

A
B
这两个类都在第三方库中。

angular typescript
7个回答
19
投票

错误消息是正确的,因为这里有一个微妙的错误。当您在父类中定义属性时,会为其每个子类自动创建该属性。

因此,在您编写的代码中,如果您为类B编写:

set/get foo(name) { ... }
,该类将具有
foo
属性无论如何,因为该属性是在
B
的父类中声明的 -
 A

在您的代码中,您实际上是在使用访问器声明破坏

foo
中声明的
A
属性。

如果你想要求

A
的所有子类都声明它们的 own
foo
属性(类似于接口),那么可以尝试:

export class A {
  abstract foo: string;
}

3
投票

另一种方法是创建一个

abstract
类,您可以使用它来扩展子类。 我使用
interface
然后使用
abstract
然后在我的最后一堂课中使用访问器(获取/设置)扩展它。

interface
中,您将定义您的属性。但是,如果您要使用访问器(获取/设置),则需要创建一个
abstract
。我只会在针对类存储和检索数据的数据类型类上执行此操作。

下面的示例可以在这里找到带有接口、抽象、扩展的 Typescript Playground 示例

interface IQuote {

    qty: number,
    rate: number | string,
    total ? : number | string
}

abstract class AQuote implements IQuote {
  abstract set qty(x: number);
  abstract get qty(): number;
  abstract set rate(x: number | string);
  abstract get rate(): number | string;
  abstract set total(x: number | string);
  abstract get total(): number | string


  protected abstract updateTotal(): void
  abstract getTotalNum(): number


}

class quote extends AQuote {

  constructor(obj: IQuote) {
    super()
    this.qty = obj.qty
    this.rate = obj.rate
  }

  set qty(v: number) {
    this._qty = v;
    this.updateTotal()
  }

  get qty() {
    return this._qty;

  }

  set rate(v: number | string) {
    this._rate = Number(v);
    this.updateTotal()
  }

  get rate() {
    return new Intl.NumberFormat('en-GB', {
      style: 'currency',
      currency: 'GBP'
    }).format(Number(this._rate))
  }

  set total(v: number | string) {
    this._total = Number(v);
  }

  get total(): number | string {
    return new Intl.NumberFormat('en-GB', {
      style: 'currency',
      currency: 'GBP'
    }).format(Number(this._total))
  }

  getTotalNum() {
    return Number(this._total)
  }

  protected updateTotal() {
    this.total = Number(this._rate) * Number(this._qty)
  }

  private _qty: number = 1;
  private _rate: number | string = 1;
  private _total ? : number | string = (Number(this._rate) * this._qty);

}



class QuoteArray extends Array < quote > {

  getTotals(): number {
    let totalFigure: number = 0
    this.forEach(o => {
      totalFigure = totalFigure + o.getTotalNum()
    });
    return totalFigure
  }

}

let arry: QuoteArray = new QuoteArray();

arry.push(new quote({
  qty: 2,
  rate: 5
}))

arry.push(new quote({
  qty: 3,
  rate: 5
}))
arry.push(new quote({
  qty: 3,
  rate: 5
}))
arry.push(new quote({
  qty: 3,
  rate: 5
}))
arry.push(new quote({
  qty: 3,
  rate: 5
}))




console.log(arry);


console.log(arry.getTotals())

上面的示例创建了一个引号数组,我可以像数组一样操作它,但我还扩展了数组类型以提供名为

getTotals()
的方法,以便它打印出格式化的货币。


1
投票

这是编程语言中的常见行为,当您从父级继承时,您不能使用相同的变量/函数/访问器名称,直到您指定新名称覆盖旧名称,但始终必须是相同的类型。因此,在您的示例中,我们在父级中有变量,在子级中有访问器。您可以进行此更改:

class A {
  _foo: string = "";
  set foo(name) {
    this._foo = name;
  }
  get foo() {
    return this._foo;
  }
}

export class B extends A {
  f1: any;
  set foo(name) {
    this.f1 = name;
  }
  get foo() {
    return this.f1;
  }
}

我相信有某种方法可以省略类型限制(如果必须的话),但我不知道如何做,我认为这不是好方法(程序应该对读者来说很清楚)。


1
投票

正如有人已经发布的,这是 TS 4 中新强制执行的错误检查(请参阅此处

但是,由于您完全处理第三方库,因此您的解决方案可能仅限于以下选项:

  1. 看看您是否可以将该第三方库升级到更新版本,与您的打字稿版本兼容
  2. 分叉该库并进行修复(创建 PR 供作者审核)。现在使用你的叉子而不是库。
  3. 将打字稿降级到此错误检查是可选的版本

1
投票

似乎在某些情况下您无法禁用此错误。

“Typescript 无法区分 d.ts 中的属性和访问器”

您可以关注以下讨论:https://github.com/microsoft/TypeScript/issues/41347

2022/05/31 修复/合并;但尚未发布 [截至 [电子邮件受保护]]


0
投票

如果您确实确定您的实现是正确的,那么您可以通过将

// @ts-nocheck
行放在 ts 文件的顶部来禁用检查。 TypeScript 文件中的 @ts-nocheck


0
投票

为了抑制我添加的警告,

"skipLibCheck": true,
到我的 tsconfig.json

这告诉 TypeScript 跳过检查第三方库中的类型,它解决了我的问题。

它也比我在这里读到的其他解决方案容易得多。

这是完整的 tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    "esModuleInterop": true,
    "target": "ES2017",
    "moduleResolution": "node",
    "sourceMap": true,
    "outDir": "dist",
    "strict": false,
    "skipLibCheck": true,
  },

  "lib": ["ES2017"]
}
© www.soinside.com 2019 - 2024. All rights reserved.