簡體   English   中英

在 React+Typescript 中合並兩種類型

[英]Merging two types in React+Typescript

我想使用組件 inheritance,允許抽象 class 中的公共狀態和繼承者中的其他狀態。 如何正確組合這兩種狀態? 我試圖省略一個外部泛型,但是當我嘗試設置狀態時仍然出現類型錯誤。

我的代碼示例:

import * as React from "react";

type CoreState = {
    coreState1: number;
    coreState2: string;
};

abstract class CoreComponent<OuterState = {}> extends React.Component<{}, Omit<OuterState, keyof CoreState> & CoreState> {
    componentDidMount() {
        this.setState({
            coreState1: 1,
            coreState2: "test",
        });
    }
}

type BaseState = {
    state1: number;
    state2: string;
};

class AnotherComponent<OuterState = {}> extends CoreComponent<Omit<OuterState, keyof BaseState> & BaseState> {
    componentDidMount() {
        super.componentDidMount();
        this.setState({
            state1: 1,
            state2: "test",
        });
    }

    render() {
        return null;
    }
}

我得到的錯誤:

TS2345: Argument of type '{ state1: 1; state2: "test"; }' is not assignable to parameter of type '(Pick<Pick<OuterState, Exclude<keyof OuterState, "state1" | "state2">> & BaseState, "state1" | "state2" | Exclude<Exclude<keyof OuterState, "state1" | "state2">, "coreState1" | "coreState2">> & CoreState) | ((prevState: Readonly<...>, props: Readonly<...>) => (Pick<...> & CoreState) | ... 1 more ... | null) | Pick<....'.   
Types of property 'state1' are incompatible.     
Type '1' is not assignable to type '(Pick<OuterState, Exclude<keyof OuterState, "state1" | "state2">> & BaseState)["state1"] | (Pick<Pick<OuterState, Exclude<...>> & BaseState, "state1" | ... 1 more ... | Exclude<...>> & CoreState)["state1"] | undefined'.

錯誤截圖: https://yadi.sk/i/ByOr05JIauRyfQ

不幸的是,編譯器無法對未指定的泛型類型執行高階類型分析,以得出結論您正在做的事情是類型安全的。 具體來說,它需要能夠驗證所有T extends objectU extends object的以下內容:

U extends Pick<Omit<T, keyof U> & U, keyof U> // 💻💥

T是未指定的泛型類型時,它無法執行此操作,例如OuterState CoreComponent<OuterState>的 class 主體內的 OuterState。 因此this.setState({ coreState1: 1, coreState2: "test" })有問題; 如果您將OuterState替換為TCoreStateU ,則會得到令人討厭的比較。

我能在 GitHub 中找到最接近規范問題的限制是microsoft/TypeScript#28884 如果您認為它足夠引人注目,您可能想要也可能不想 go 解決該問題並給出或解釋您的用例。 但目前尚不清楚這是否會得到解決,或者何時會發生。 現在,接受這是 TypeScript 中的限制是有意義的。


因此,一旦您發現自己對未指定的泛型類型進行類型操作,您可能需要使用類型斷言等來克服編譯器高階類型分析的缺陷。

例如,您可以這樣做:

type Merge<T, U> = Omit<T, keyof U> & U;
type PickMerge<T, U> = Pick<Merge<T, U>, keyof U>;

abstract class CoreComponent<S = {}> extends React.Component<{}, Merge<S, CoreState>> {
    componentDidMount() {
        this.setState({
            coreState1: 1,
            coreState2: "test",
        } as PickMerge<S, CoreState>); // assert here
    }
}

我創建了MergePickMerge ,這樣您就不必重復自己。 類型斷言只是告訴編譯器,我們確定 object 文字的類型是PickMerge<S, CoreState>並且它不應該擔心驗證它。 相似地:

class AnotherComponent<S = {}> extends CoreComponent<Merge<S, BaseState>> {
    componentDidMount() {
        super.componentDidMount();
        this.setState({
            state1: 1,
            state2: "test",
        } as PickMerge<Merge<S, CoreState>, BaseState>); // assert here
    }
}

您可以看到這是可行的,盡管它開始變得乏味。 你總是可以說as any並放棄更多的類型安全以換取方便。


另一種可能性是某種重構,無論是發出的 JS 還是類型,但這在很大程度上取決於您的用例。 例如,您可以說您的組件的stateCoreStateMerge<S, CoreState>的聯合,這樣編譯器很樂意允許指定CoreState

abstract class CoreComponentAlternate<S = {}> extends
    React.Component<{}, CoreState | Merge<S, CoreState>> {
    componentDidMount() {
        this.setState({
            coreState1: 1,
            coreState2: "test",
        });
    }
}

但當然,這可能與您的代碼庫的 rest 交互不佳。 如果是這樣,斷言可能是您能夠實現的最好的。

Playground 代碼鏈接

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM