为什么我不能在TypeScript 2.4+中"实现"全可选接口?

Rya*_*ugh 4 class typescript

我写了一些代码:

interface IEventListener {
    onBefore?(name: string): void;
    onAfter?(name: string): void;
}

class BaseListener implements IEventListener {
    stuff() { 

    }
}
Run Code Online (Sandbox Code Playgroud)

这里的意图是有人可以从BaseListener他们的onBefore/ onAfter方法中获得并获得正确的类型检查:

class DerivedListener extends BaseListener {
    // Should be an error (name is string, not number)
    onBefore(name: number) {

    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我没有收到错误DerivedListener.相反,我得到一个错误BaseListener:

类型"BaseListener"没有与"IEventListener"类型相同的属性

这是怎么回事?

Rya*_*ugh 6

implementsTypeScript中的子句完成一件事:它确保声明类可分配给已实现的接口.换句话说,当您编写时class BaseListener implements IEventListener,TypeScript会检查此代码是否合法:

var x: BaseListener = ...;
var y: IEventListener = x; // OK?
Run Code Online (Sandbox Code Playgroud)

因此,当您编写时class BaseListener implements IEventListener,您可能打算做的是"将"可选属性"复制" IEventListener到类声明中.

相反,什么也没发生.

TypeScript 2.4改变了所有可选类型的工作方式.以前,任何没有冲突类型属性的类型都可以分配给全可选类型.这导致允许各种恶作剧:

interface HttpOptions {
  method?: string;
  url?: string;
  host?: string;
  port?: number;
}
interface Point {
  x: number;
  y: number;
}
const pt: Point = { x: 2, y: 4 };
const opts: HttpOptions = pt; // No error, wat?
Run Code Online (Sandbox Code Playgroud)

从2.4开始的新行为是全可选类型要求源类型至少有一个匹配属性,以使类型兼容.这会捕获上述错误并正确地指出您尝试了implements一个界面而没有实际执行任何操作.

相反,您应该使用声明合并来将接口成员"复制"到您的类中.这就像编写具有相同名称的接口声明(以及相同的类型参数,如果有的话)一样简单:

interface IEventListener {
    onBefore?(name: string): void;
    onAfter?(name: string): void;
}

class BaseListener {
    stuff() { 

    }
}
interface BaseListener extends IEventListener { }
Run Code Online (Sandbox Code Playgroud)

这将导致属性IEventListener也在BaseListener,并正确标记DerivedListener原始帖子中的错误.