简体   繁体   English

在 React+Typescript 中合并两种类型

[英]Merging two types in React+Typescript

I want to use component inheritance, allowing common states in the abstract class and other ones in the inheritor.我想使用组件 inheritance,允许抽象 class 中的公共状态和继承者中的其他状态。 How do I combine this two types of states correctly?如何正确组合这两种状态? I am trying to Omit an external generic, but I still get a type error when i try to set states.我试图省略一个外部泛型,但是当我尝试设置状态时仍然出现类型错误。

My code example:我的代码示例:

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;
    }
}

Error i got:我得到的错误:

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'.

Error screenshot: https://yadi.sk/i/ByOr05JIauRyfQ错误截图: https://yadi.sk/i/ByOr05JIauRyfQ

The compiler unfortunately cannot perform the kind of higher-order type analysis on unspecified generic types necessary to conclude that what you are doing is type safe.不幸的是,编译器无法对未指定的泛型类型执行高阶类型分析,以得出结论您正在做的事情是类型安全的。 Specifically, it would need to be able to verify the following for all T extends object and U extends object :具体来说,它需要能够验证所有T extends objectU extends object的以下内容:

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

It cannot do this when T is an unspecified generic type, such as OuterState inside the class body of CoreComponent<OuterState> .T是未指定的泛型类型时,它无法执行此操作,例如OuterState CoreComponent<OuterState>的 class 主体内的 OuterState。 And therefore has a problem with this.setState({ coreState1: 1, coreState2: "test" }) ;因此this.setState({ coreState1: 1, coreState2: "test" })有问题; if you substitute OuterState for T and CoreState for U , you get the offending comparison.如果您将OuterState替换为TCoreStateU ,则会得到令人讨厌的比较。

The closest I can find to canonical issue in GitHub for limitation is microsoft/TypeScript#28884 .我能在 GitHub 中找到最接近规范问题的限制是microsoft/TypeScript#28884 You may or may not want to go to that issue and give it a or explain your use case if you think it's compelling enough.如果您认为它足够引人注目,您可能想要也可能不想 go 解决该问题并给出或解释您的用例。 But it's not obvious that this will ever be addressed, or when it would happen if so.但目前尚不清楚这是否会得到解决,或者何时会发生。 For now it makes sense to just accept that this is a limitation in TypeScript.现在,接受这是 TypeScript 中的限制是有意义的。


Therefore, once you find yourself doing type manipulation on unspecified generic types, you will probably need to use type assertions or the like to overcome the deficiencies in the compiler's higher order type analysis.因此,一旦您发现自己对未指定的泛型类型进行类型操作,您可能需要使用类型断言等来克服编译器高阶类型分析的缺陷。

For example, you could do this:例如,您可以这样做:

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
    }
}

I've created Merge and PickMerge so that you don't have to repeat yourself as much.我创建了MergePickMerge ,这样您就不必重复自己。 The type assertion just says to the compiler that we are sure that the object literal is of type PickMerge<S, CoreState> and that it shouldn't worry about verifying it.类型断言只是告诉编译器,我们确定 object 文字的类型是PickMerge<S, CoreState>并且它不应该担心验证它。 Similarly:相似地:

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
    }
}

You can see that this works, although it is starting to become tedious.您可以看到这是可行的,尽管它开始变得乏味。 You could always just say as any and give up a little more type safety in exchange for convenience.你总是可以说as any并放弃更多的类型安全以换取方便。


The other possibility would be some sort of refactoring, either of the emitted JS or of the types, but that strongly depends on your use cases.另一种可能性是某种重构,无论是发出的 JS 还是类型,但这在很大程度上取决于您的用例。 For example, you might be able to get away with saying that your component's state is a union of CoreState and Merge<S, CoreState> , so that the compiler is happy to allow a CoreState to be specified:例如,您可以说您的组件的stateCoreStateMerge<S, CoreState>的联合,这样编译器很乐意允许指定CoreState

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

But of course this may interact poorly with the rest of your code base.但当然,这可能与您的代码库的 rest 交互不佳。 If so, assertions are probably the best you will be able to achieve.如果是这样,断言可能是您能够实现的最好的。

Playground link to code Playground 代码链接

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM