问题来源于 React.component的第二个参数的类型定义问题,我构建了如下简化demo,方便描述问题:函数
class P<STATE> { public state: STATE; } interface Obj { arr: Obj[]; } class Test1 extends P<Obj> { public state = {arr: []}; func(obj: Obj) { this.state.arr.push(obj);// 这里ts在obj上抛错 Error:(51, 29) TS2345: Argument of type 'Obj' is not assignable to parameter of type 'never'. } }
这里主要产生的问题是,咱们认为 this.state.arr 应该是Obj[] 类型,因此能够往里面push进去Obj类型的数据,然而this.state.arr却被识别为never[]类型,因此push会抛错。this
分析后,发现虽然Test1的state在设置默认值的时候能够使用父类 P 中state的类型定义,可是,在函数中 this.state的类型定义倒是使用 默认值 {arr: []} 的类型推断。推断出的类型为{arr:never[]}。spa
因此产生以上问题。code
因而,咱们能够这样处理:component
class Test2 extends P<Obj> { public state: Obj = {arr: []}; // 子类同时定义state类型为Obj func(obj: Obj) { this.state.arr.push(obj); } }
可是这就致使Obj须要定义两次。blog
咱们又注意到,在构造函数中直接赋值,也能够解决该问题:继承
class Test3 extends P<Obj> { constructor() { super(); this.state = {arr: []}; // 直接在constructor中赋值 } func(obj: Obj) { const str: string = this.state; this.state.arr.push(obj); } }
可是写起来是比较麻烦的,每次都要写构造函数。string
最后,咱们按赋值的思想,考虑使用中间类方式:it
// 中间类,继承P<T> 这里P类一般为第三方类,如React.Component abstract class Middle<T> extends P<T> { protected constructor() { super(); if (this.initState) { this.state = this.initState(); } } abstract initState(): T; } class Test2 extends Middle<Obj> { initState() {// 经过方法来初始化state return {arr: []}; } func(obj: Obj) { const str: string = this.state; this.state.arr.push(obj); } }
咱们在中间类中定义了构造函数来默认调用initState函数,而后让state的初始值做为函数的返回值,能够解决属性默认值的类型覆盖了父类对属性参数的定义。class
引进来的代价是咱们须要一个中间类。
不过,考虑中间类的引入,能够带来比较多的扩展性,权衡之下,仍是可取的。