带有约束的打字稿泛型不能分配给泛型接口

Posted

技术标签:

【中文标题】带有约束的打字稿泛型不能分配给泛型接口【英文标题】:Typescript generic with constraint isn't assignable to generic interface 【发布时间】:2018-02-09 06:06:20 【问题描述】:

我在 TypeScript 2.4.2 的泛型类中遇到错误,其约束与不太严格的接口不兼容。我收到以下错误:

ts/components/Schedule.ts(37,13): error TS2322: Type ' personWeekView: PlanItemScheduleView; projectWeekView:PlanItemScheduleView; r...' 不能分配给类型 'Map'。 属性“personWeekView”与索引签名不兼容。 类型“PlanItemScheduleView”不可分配给类型“IPlanItemScheduleView”。 属性“onAddedItem”的类型不兼容。 类型 '(item: T, initial: boolean) => void' 不可分配给类型 '(item: T, initial: boolean) => void'。 参数“item”和“item”的类型不兼容。 类型“T”不可分配给类型“PlanItem”。

ts/views/PlanItemScheduleView.ts(2,18):错误 TS2420:“PlanItemScheduleView”类错误地实现了“IPlanItemScheduleView”接口。 属性“onAddedItem”的类型不兼容。 类型 '(item: T, initial: boolean) => void' 不可分配给类型 '(item: T, initial: boolean) => void'。 参数“item”和“item”的类型不兼容。 类型“T”不可分配给类型“PlanItem”。

ts/views/PlanItemScheduleView.ts(99,79):错误 TS2345:“this”类型的参数不可分配给“IControllerListener”类型的参数。 类型“PlanItemScheduleView”不可分配给类型“IControllerListener”。 属性“onAddedItem”的类型不兼容。 类型 '(item: T, initial: boolean) => void' 不可分配给类型 '(item: T, initial: boolean) => void'。 参数“item”和“item”的类型不兼容。 类型“T”不可分配给类型“PlanItem”。

接口

namespace Planning 
    export interface IPlanItemScheduleView extends IView, IControllerListener<IPlanItem> 
        setTimespan(timespan: Timespan): void;
        getName(): string;
    

namespace Planning 
    export interface IControllerListener<T> 
    /**
     * Notifies the listener that an item is added to the cache so it can add it to its view.
     * 
     * @template T
     * @param T item
     * @param boolean initial
     * 
     * @memberOf IControllerListener
     */
    onAddedItem<T>(item: T, initial: boolean): void;
    


namespace Planning 
    export class PlanItemScheduleView<T extends PlanItem> implements IPlanItemScheduleView 

        public onAddedItem<T extends PlanItem>(item: T, initial: boolean): void 
            // implementation that needs properties on PlanItem
        
    

PlanItem 是一个抽象基类,由一些实际实现继承。我有几种不同的观点,我是这样构建的:

  // Create the different views
  this._views = 
            personWeekView: new PlanItemScheduleView<Person>(this._options, this._logger, this),
            projectWeekView: new PlanItemScheduleView<Project>(this._options, this._logger, this),
            resourceWeekView: new PlanItemScheduleView<Resource>(this._options, this._logger, this),
        ;

我以为我之前在其他版本的 tsc 中进行过编译,但我可能弄错了。我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

onAddedItem 不需要泛型参数,如果您希望onAddedItem 采用与类相同的参数类型,则可以使用类参数。您可以将IPlanItemScheduleView 设为泛型,以便将PlanItemScheduleView 类型参数向下传递给IControllerListener

export interface IControllerListener<T> 
  onAddedItem(item: T, initial: boolean): void;

export interface IPlanItemScheduleView<T extends IPlanItem>  extends IControllerListener <T> 


export class PlanItemScheduleView<T extends PlanItem> implements IPlanItemScheduleView<T> 
  public onAddedItem(item: T, initial: boolean): void 
      // implementation that needs properties on PlanItem
  

注意:已编辑以考虑反馈。

【讨论】:

这使得 onAddedItem 不太安全,因为根据您的建议,我可以为 onAddedItem 提供不同的派生类型,而不是将其缩小到仅在构造时提供的一种类型,这实际上是使用泛型的原因/模板类。 您的版本对类型的限制不超过传递给类的类型。您是说您希望 onAddedItem 接受从 PlanItem 派生的类型,但不接受 PlanItem 本身 是的,对于 personWeekView 实例,我只希望 Person 成为参数的类型,而不是任何其他派生的 PlanItem 类型。 我改了答案,现在onAddedItem和类的参数类型一样,所以不能调用_views.personWeekView.onAddItem(new Project())

以上是关于带有约束的打字稿泛型不能分配给泛型接口的主要内容,如果未能解决你的问题,请参考以下文章

为啥类型不能分配给泛型类型

打字稿泛型-“扩展对象”毫无意义吗?最佳做法是啥?

打字稿泛型类型 T toString

2018-7-26-随笔-泛型

typescript 打字稿泛型示例

typescript 打字稿泛型