TS2611: 'foo' is defined as a property in class 'A', but is overridden here in 'B' as an accessor

Viewed 17899

I am facing a Typescript compilation error in my Angular app. Following is the sample code and error message.

Would you please guide me to suppress this error. I think this code is correct as per the 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;
  }
}

Editor's note: in the comments it emerged that both classes A and B are in third-party libraries.

6 Answers

The error message is correct as there is a subtle bug here. When you define a property in a parent class, the property is created automatically for every one of its sub-classes.

So, in the code you wrote, had you not written for class B: set/get foo(name) { ... } the class would have had the foo property anyway since the property is declared in B's parent class - A.

In your code you're actually clobbering the foo property declared in A with the accessor declarations.

If you want to require all sub-classes of A to declare their own foo property (similar to an interface), then try:

export class A {
  abstract foo: string;
}

Another way to do this is to create an abstract class that you can use to extend your sub-classes. I use an interface then an abstract then extend it in my final class like so with the accessors (get/set).

In the interface you will define your properties. However, if you're going to use accessors (get/set) you need to create an abstract. I would do this only on data type classes where you store and retrieve data against a class.

The example below can be found here Typescript playground example with interface, abstract, extend

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())

The example above creates an Array of quotes that I can manipulate like an array but I have also extended the Array type to give me method called getTotals() so that it prints out a formatted currency.

It is usual behavior in programming languages, when you inherit from parent you can't use same variable/function/accessor names until you specify that new one override old one, but always this must be the same type. So in your example we have variable in parent and accessor in child. You can do this change:

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;
  }
}

I believe there is some way to omit type limitations (if you have to) but I don't know how and I think that is not good way (program should be clear for reader).

As someone posted already, this is newly enforced error checking in TS 4 (see here)

However, since you're dealing entirely with 3rd-party libraries, your solution is limited to probably these options:

  1. See if you can upgrade that 3rd-party library to a newer version, compatible with your version of typescript
  2. Fork that library and make the fixes (create a PR for the author to review). Use your fork for now instead of the library.
  3. Downgrade typescript to a version where this error checking is optional

Its seems there are cases where you cannot disable this error.

"Typescript cannot distinguish property from accessor in d.ts"

You can follow the discussion at: https://github.com/microsoft/TypeScript/issues/41347

Fixed/Merged 2022/05/31; but not yet released [as of typescript@4.7.4]

If you're really sure that your implementation is correct then you can disable the check by putting // @ts-nocheck line at the top of the ts file. @ts-nocheck in TypeScript Files

Related