問題來源於 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
引進來的代價是咱們須要一箇中間類。
不過,考慮中間類的引入,能夠帶來比較多的擴展性,權衡之下,仍是可取的。