为什么TypeScript会拒绝实现仅包含可选成员的接口的类?
这个问题我之前踩过坑,一开始也满脑子问号——接口里全是可选成员,我实现它的时候不写这些成员怎么还报错?后来摸透TypeScript的类型检查逻辑才搞明白。
说白了,TypeScript这里是在做结构兼容性的严格校验:当你用implements声明类要实现某个接口时,它会检查类和接口之间有没有「共同语言」。如果接口里全是可选成员,而你的类完全没实现这些成员,也没有任何和接口重叠的属性/方法,TypeScript就会判定这俩类型「毫无关联」,直接抛出2559错误——它怕你是手滑写错了接口名,或者误把完全不相关的类和接口绑定到一起。
拿你的代码举例子:
// 全是可选成员的接口 interface IController { $onInit?(): void; $doCheck?(): void; $onChanges?(): void; $onDestroy?(): void; $postLink?(): void; } // 这个类和IController没有任何重叠成员 class Foo implements IController { constructor() {} myFunction() {} }
你的Foo类里只有构造函数和myFunction,和IController里的可选方法一个都不沾边。TypeScript这时候就会想:「你说你实现了IController,但你俩根本不是一路人啊?该不会是写错了吧?」于是就报错了。
那怎么解决呢?给你几个实用的办法:
加个空的可选方法实现:最简单的就是在
Foo里加一个接口里的可选方法,哪怕是空实现也行,比如:class Foo implements IController { constructor() {} myFunction() {} // 加个空的$onInit,让TypeScript知道你和IController有关联 $onInit() {} }这样既满足了TypeScript的校验,也不影响AngularJS的控制器逻辑。
用类型断言兜底:如果你实在不想加空方法,可以在使用类实例的时候用类型断言,把它转成
IController类型:const myController = new Foo() as IController;不过这种办法会跳过类型检查,除非万不得已不推荐用,容易埋下隐性bug。
临时忽略错误(不推荐):用
// @ts-ignore注释掉错误提示,但这相当于直接关掉了TypeScript的防护,尽量别这么干。
其实TypeScript的这个设计是为了帮你避免低级错误——比如你本来想实现IUserController,结果手滑写成了IController,如果没有这个校验,你根本发现不了这个笔误,等到运行时出问题就麻烦了。
内容来源于stack exchange

