typescript 關於class屬性類型定義被屬性默認值覆蓋的問題及解決方式

問題來源於 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

引進來的代價是咱們須要一箇中間類。

不過,考慮中間類的引入,能夠帶來比較多的擴展性,權衡之下,仍是可取的。

相關文章
相關標籤/搜索