【问题标题】:Why can't I use a union type with redux connect?为什么我不能在 redux connect 中使用联合类型?
【发布时间】:2022-02-12 17:14:34
【问题描述】:

这是我正在尝试做的一个简短示例:

import { connect } from "react-redux";

interface ErrorProps {
  error: true;
  description: string;
}

interface NoErrorProps {
  error: false;
}

type TestProps = ErrorProps | NoErrorProps;

function Test(props: TestProps) {
  return props.error ? <>Error: ${props.description}</> : <>OK</>;
}

// This is a test implementation that always returns no error
connect(() => ({ error: false }))(Test);

但是,我得到一个错误:

Argument of type '(props: TestProps) => Element' is not assignable to parameter of type 'ComponentType<Matching<{ error: boolean; } & DispatchProp<AnyAction>, TestProps>>'.
  Type '(props: TestProps) => Element' is not assignable to type 'FunctionComponent<Matching<{ error: boolean; } & DispatchProp<AnyAction>, TestProps>>'.
    Types of parameters 'props' and 'props' are incompatible.
      Type 'PropsWithChildren<Matching<{ error: boolean; } & DispatchProp<AnyAction>, TestProps>>' is not assignable to type 'TestProps'.
        Type 'Matching<{ error: boolean; } & DispatchProp<AnyAction>, NoErrorProps> & { children?: ReactNode; }' is not assignable to type 'TestProps'.
          Type 'Matching<{ error: boolean; } & DispatchProp<AnyAction>, NoErrorProps> & { children?: ReactNode; }' is not assignable to type 'NoErrorProps'.
            Types of property 'error' are incompatible.
              Type 'boolean' is not assignable to type 'false'.

现在,我已经使用代数数据类型 很长时间 了,并且通常对自己理解自己在做什么很有信心。但在这里,我会用(可能)非常明显的细节来解释我的推理,因为我很难过并怀疑我误解了一些非常基本和根本的东西。

我正在尝试解读此错误消息,虽然我不完全确定类型比较左侧发生了什么(使用 Redux 类型),但似乎每一行都是它前面的行的原因.所以,我看这两行,忽略左边的类型:

   Type '...' is not assignable to type 'TestProps'.
     Type '...' is not assignable to type 'NoErrorProps'.

而且我注意到这意味着无论 '...' 意味着它不会改变,所以我在这里忽略它。但是这个序列不能分配给TestProps因为它不能分配给'NoErrorProps'。这意味着要分配给TestProps,它必须可以分配NoErrorProps

这没有任何意义。我刚刚检查过我仍然神志清醒:

const _1: TestProps = { error: false };
const _2: TestProps = { error: true, description: "foo" };

第一个变量不能分配给ErrorProps,第二个变量不能分配给NoErrorProps,但两者都可以完全分配给TestProps,因为这就是联合类型的工作方式。为什么上面错误信息中的行暗示相反?

【问题讨论】:

  • 虽然这不是您问题的直接答案:我们强烈建议今天使用 React-Redux 挂钩 API 而不是 connect,这是主要原因之一这是因为 TS 类型大大更易于使用。请考虑改用挂钩。
  • 这真是一件很奇怪又迂回的事情,所以我不知道我是否能雄辩地解释它。 TypeScript 中有时会出现无法将联合分配给自身的问题,因为联合类型 TestProps 不能分配给联合的任何单个成员(ErrorPropsNoErrorProps)。这不是错误connect((): NoErrorProps =&gt; ({ error: false }))(Test);,而是connect((): TestProps =&gt; ({ error: false }))(Test);
  • connect HOC 的类型非常复杂,并且依赖于大量的反向推理。例如,错误消息中显示的Matching 类型是type Matching&lt;InjectedProps, DecorationTargetProps&gt; = { [P in keyof DecorationTargetProps]: P extends keyof InjectedProps ? InjectedProps[P] extends DecorationTargetProps[P] ? DecorationTargetProps[P] : InjectedProps[P] : DecorationTargetProps[P]; }。而这只是其中的一部分!
  • @markerikson 感谢您的推荐,我会记住这一点!但在这种情况下,我正在处理一个已经相当大的项目,并且在整个过程中对其进行重构是一个相当大的项目

标签: reactjs typescript redux algebraic-data-types union-types


【解决方案1】:

我可能弄错了,但我认为这完全是关于逆变的。

TS 编译器抱怨 (props: TestProps) =&gt; Element 不能分配给 (props: {error:boolean}) =&gt; Element

更简单的例子:

type UnionFn = (props: TestProps) => Element;
type SingleFn = (props: { error: boolean }) => Element;

declare let unionFn: UnionFn;
declare let singleFn: SingleFn;

unionFn = singleFn; // ok
singleFn = unionFn; // error

这可能很奇怪,因为TestProps 可以分配给{error: boolean}

declare let union: Union;
declare let single: Single;

union = single // expected error
single = union; // ok

但是,当这些类型/值在参数位置时,继承的箭头会变成相反的方向,因为参数在逆变位置。这意味着singleFn 可以分配给unionFn,而unionFn 不能再分配给singleFn

因此,我认为问题不在于 connect 函数类型。

有关 *-variance 的更多信息,您可以找到 here

Here您可以通过更多示例找到我的答案

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-11-29
    • 1970-01-01
    • 2018-09-17
    • 1970-01-01
    • 2019-07-24
    • 2014-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多