Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Property in type is not assignable to the same property in base type

Hi I've a basic example where I keep getting this error and I am not sure what exactly the problem is.

Basically I want to pass different types to class A at runtime. In the given example it's Order but I will write other classes for Product, Customers and more and for all of them I want to use A<T> as parent. I've also added the real code below the minimal example.

Can please someone explain the solution and the problem to this.

interface Order{
    customer: {
        name: string;
    };
}

abstract class A<T>{
    protected abstract matches<T>(tableData: T, term: string): boolean;
}

class B extends A<Order>{

    protected matches<T extends Order>(tableData: T, term: string): boolean {
        return tableData.customer.name.toLowerCase().includes(term.toLowerCase());
    }
}

The goal is to access tableData.customer within class B. I want to pass Order to the matches method as a Generic type but I am not sure how this can be implemented the right way.

Update

Parent Class:

export abstract class TableFilterService<T> {
  private _loading$ = new BehaviorSubject<boolean>(true);
  private _search$ = new Subject<void>();
  private _total$ = new BehaviorSubject<number>(0);
  private _tableData$: BehaviorSubject<T[]> = new BehaviorSubject([]);
  private _data$!: T[];

  protected _state: State = {
    page: 1,
    pageSize: 50,
    searchTerm: '',
    sortColumn: '',
    sortDirection: ''
  };

  private compare = (v1: string | number, v2: string | number) => v1 < v2 ? -1 : v1 > v2 ? 1 : 0;
  protected abstract matches<T>(tableData: T, term: string, pipe: PipeTransform): boolean;

  get tableData$() { return this._tableData$.asObservable(); }
  get search$() { return this._search$.asObservable(); }
  get total$() { return this._total$.asObservable(); }
  get loading$() { return this._loading$.asObservable(); }
  get page() { return this._state.page; }
  get pageSize() { return this._state.pageSize; }
  get searchTerm() { return this._state.searchTerm; }
  get data() { return this._data$; }

  set data(data: T[]) { this._data$ = data; }
  set page(page: number) { this._set({page}); }
  set pageSize(pageSize: number) { this._set({pageSize}); }
  set searchTerm(searchTerm: string) { this._set({searchTerm}); }
  set sortColumn(sortColumn: SortColumn) { this._set({sortColumn}); }
  set sortDirection(sortDirection: SortDirection) { this._set({sortDirection}); }

  constructor(protected pipe: DecimalPipe) {

    this._search$.pipe(
      tap(() => this._loading$.next(true)),
      debounceTime(200),
      switchMap(() => this._search()),
      delay(200),
      tap(() => this._loading$.next(false))
    ).subscribe(result => {
      this._tableData$.next(result.tableData);
      this._total$.next(result.total);
    });

    this._search$.next();
  }

  protected sort<T>(orders: T[], column: SortColumn, direction: string): T[] {
    if (direction === '' || column === '') {
      return orders;
    } else {
      return [...orders].sort((a: any, b: any) => {
        const res = this.compare(a[column], b[column]);
        return direction === 'asc' ? res : -res;
      });
    }
  }

  protected _search(): Observable<SearchResult<T>> {
    const {sortColumn, sortDirection, pageSize, page, searchTerm} = this._state;

    // 1. sort
    let tableData = this.sort(this.data, sortColumn, sortDirection);

    // 2. filter
    tableData = tableData.filter(data => this.matches(data, searchTerm, this.pipe));
    const total = tableData.length;

    // 3. paginate
    tableData = tableData.slice((page - 1) * pageSize, (page - 1) * pageSize + pageSize);

    return of({ tableData, total});
  }

  private _set(patch: Partial<State>) {
    Object.assign(this._state, patch);
    this._search$.next();
  }
}

Child Class:

export class OrderTableService extends TableFilterService<Order>{

  protected matches(tableData: Order, term: string, pipe: PipeTransform): boolean {
    return tableData.customer.name.toLowerCase().includes(term.toLowerCase());
  }
}

Error:

enter image description here

The complete error:

Property 'matches' in type 'OrderTableService' is not assignable to the same property in base type 'TableFilterService<Order>'. Type '(tableData: Order, term: string, pipe: PipeTransform) => boolean' is not assignable to type '<T>(tableData: T, term: string, pipe: PipeTransform) => boolean'. Types of parameters 'tableData' and 'tableData' are incompatible. Type 'T' is not assignable to type 'Order'.ts

like image 201
Mj1992 Avatar asked Dec 06 '25 02:12

Mj1992


1 Answers

Your abstract function generic matches<T> overrides generic T of the class A<T>. What you wrote is basically this:

abstract class A<T>{
    protected abstract matches<U>(tableData: U, term: string): boolean;
}

The generic T at the top of the class was basically useless. The solution could look like this:

interface Order{
    customer: {
        name: string;
    };
}

abstract class A<T>{
    // No generic <T> at the function itself anymore. Now tableData: T refers to A<T>
    protected abstract matches(tableData: T, term: string): boolean;
}

class B extends A<Order>{

    protected matches(tableData: Order, term: string): boolean {
        return tableData.customer.name.toLowerCase().includes(term.toLowerCase());
    }
}

class C extends A<Order>{

    protected matches<T extends Order>(tableData: T, term: string): boolean {
        return tableData.customer.name.toLowerCase().includes(term.toLowerCase());
    }
}
like image 64
Mirco S. Avatar answered Dec 08 '25 14:12

Mirco S.



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!