【问题标题】:Property in type is not assignable to the same property in base type类型中的属性不能分配给基类型中的相同属性
【发布时间】:2021-06-22 12:32:30
【问题描述】:

您好,我有一个基本示例,我不断收到此错误,但我不确定到底是什么问题。

基本上我想在运行时将不同的类型传递给class A。在给定的示例中,它是Order,但我将为ProductCustomers 等编写其他类,并且对于所有这些类,我想使用A<T> 作为父类。我还在最小示例下方添加了真实代码。

请有人解释一下解决方案和问题。

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

目标是在class B 内访问tableData.customer。我想将Order 作为通用类型传递给matches 方法,但我不确定如何以正确的方式实现。

更新

父类:

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

儿童班:

export class OrderTableService extends TableFilterService<Order>{

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

错误:

完整的错误:

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

【问题讨论】:

  • 为什么您的matches 方法需要一个类型参数T extends Order,而这仅用于参数?如果它的参数类型只是Order,则该方法将在完全相同的情况下可用,因为T 类型的任何参数都是Order 的子类型,无论如何都可以分配给Order 类型的参数。
  • Order 只是一种类型,我还必须传递更多类型。我会有更多像ProductTableService 这样的类,其中传递给matches 的类型是Product
  • 如果ProductTableServiceOrder 的子类型,那么您已经可以传递它而无需泛型类型。如果它不是Order的子类型,那么你不能传递它,因为它不满足类型参数的上限。

标签: typescript typescript-generics


【解决方案1】:

您的抽象函数泛型matches&lt;T&gt; 覆盖类A&lt;T&gt; 的泛型T。你写的基本上是这样的:

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

类顶部的通用T 基本上没用。解决方案可能类似于 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());
    }
}

【讨论】:

  • 您好,感谢您的回答。问题是class A 有很多通用的东西,它们不仅会被Order 使用,还会被其他接口使用,例如Product。如果我在 A 类定义中写 T extends order,那可能会使我的代码不那么通用。
  • 这是我的复制错误,已经在更新中删除了。
  • 很奇怪我试过你的例子,但编译器仍然抱怨,检查更新。你知道可能是什么原因,我之前也试过你的例子,我认为我可能做错了什么。编译器一直报错
  • TableFilterService 定义如何。如果您检查我链接的游乐场,您会发现它适用于您的简单定义。需要更多信息来帮助您。
  • 正如我在答案的第一部分中所写的,匹配项的 正在影响课堂上的 。替换受保护的抽象匹配(tableData: T, term: string, pipe: PipeTransform): boolean;带有受保护的抽象匹配(tableData:T,术语:字符串,管道:PipeTransform):布尔值;。我还用代码中的注释更新了答案,以使其更加清晰。
猜你喜欢
  • 1970-01-01
  • 2019-09-09
  • 1970-01-01
  • 2018-08-25
  • 2021-12-12
  • 2021-10-28
  • 2023-02-13
  • 2020-08-23
  • 1970-01-01
相关资源
最近更新 更多