【问题标题】:React TS: Can I do this without typecasting? "could be instantiated with an arbitrary type which could be unrelated"React TS:我可以在不进行类型转换的情况下做到这一点吗? “可以用可能不相关的任意类型实例化”
【发布时间】:2021-06-23 21:41:04
【问题描述】:

我有这段代码(这是我真实代码的简化版本,希望我没有删除任何重要的东西)

import React, { ReactElement, ReactNode, createElement } from "react";

interface WithChildren {
  children: ReactNode;
}

type WrapperType<WTProps> = (
  props: WithChildren & WTProps
) => ReactElement | null;

interface SetProps<P> {
  wrap: WrapperType<P>;
  children: ReactNode;
  [x: string]: unknown;
}
export function Set<WProps>(props: SetProps<WProps>) {
  const { wrap, children, ...rest } = props;

  return createElement(wrap, { ...rest, children });
}

interface WrapperProps {
  children: ReactNode;
  foo: string;
}
const ChildWrapper: React.FC<WrapperProps> = ({ foo, children }) => {
  return (
    <div>
      <h1>ChildWrapper</h1>
      <p>{foo}</p>
      {children}
    </div>
  );
};

export default function App() {
  return (
    <Set wrap={ChildWrapper} foo="bar">
      <h1>Hello CodeSandbox</h1>
    </Set>
  );
}

createElement 行给出了这个错误

No overload matches this call.
  The last overload gave the following error.
    Argument of type '{ children: React.ReactNode; }' is not assignable to parameter of type 'Attributes & WithChildren & WProps'.
      Type '{ children: React.ReactNode; }' is not assignable to type 'WProps'.
        'WProps' could be instantiated with an arbitrary type which could be unrelated to '{ children: React.ReactNode; }'

我可以通过这样的类型转换来“修复”它

return createElement(wrap, { ...rest, children } as WProps & WithChildren);

但这消除了类型安全性。

我发现了这个类似的问题ts: 'Props' could be instantiated with an arbitrary type which could be unrelated to another type,他们使用Omit&lt;&gt; 来解决它。但我不知道如何在我的情况下应用它。

有没有什么方法可以在不进行类型转换的情况下做到这一点?

编辑:这是一个代码框,显示错误https://codesandbox.io/s/optimistic-microservice-ck7nf?file=/src/App.tsx

以及错误信息的截图

【问题讨论】:

  • 你的代码中好像没有这个错误
  • @zixiCat 我添加了一个代码框链接,您可以在其中看到错误
  • 我开始给你写一个很长的答案,但实际上我现在所处的位置 children 的类型被推断为 Set 的孩子的类型,而不是ChildWrapper 所以WrapperProps 必须是JSX.Element 而不是ReactNodetsplay.dev/WKkpzW我看看能不能更好的控制推理。

标签: reactjs typescript generics typescript-typings typescript-generics


【解决方案1】:

哦,我改变了很多。至于为什么初始代码会出错,我对此感到有些困惑。 也许Set&lt;WProps&gt;(props: SetProps&lt;WProps&gt;) 中的WProps 无法从实例中获取正确的初始类型。

我的解决方案:

import React, { ReactElement, ReactNode } from 'react';

export function Set<WProps>(
  props: WProps & {
    wrap: (props: WProps) => ReactElement;
  }
) {
  const { wrap: Wrap } = props;
  return <Wrap {...props} />;
}

interface WrapperProps {
  children: ReactNode;
  foo: string;
}

const ChildWrapper = ({ foo, children }: WrapperProps) => {
  return (
    <div>
      <h1>ChildWrapper</h1>
      <p>{foo}</p>
      {children}
    </div>
  );
};

export default function App() {
  return (
    <Set<WrapperProps> wrap={ChildWrapper} foo={'1'}>
      <h1>Hello CodeSandbox</h1>
    </Set>
  );
}

我的tsconfig中的strictFunctionTypes属性设置为false(默认值),所以没有复现。

【讨论】:

  • 非常感谢!但是,我的设计目标之一是要求所有包装器都将children 作为道具。在这里,我们可以从WrapperProps 中删除children,并从ChildWrapper 中删除对儿童的支持,它仍然可以编译。这就是为什么我有一个单独的WithChildren 接口,我加入了WTProps。可以在这里强制执行吗?
  • @Tobbe:不,这是另一个问题,如下图所示,点击more detail about this _________________________________________________________________const a: (p: string) =&gt; void = () =&gt; { }; // No error thrown
  • 之前的代码有错误而你没有的原因是wrap之前被从props中删除了,如果WProps包含wrap,这是一个潜在的问题。您正在传递整个props 对象,我们知道它可以分配给WProps,因此可以修复错误:)
  • 另外你也不需要&lt;Set&lt;WrapperProps&gt;&gt;里面的&lt;WrapperProps&gt;注解。看起来它自己推断得很好。
猜你喜欢
  • 2021-10-26
  • 1970-01-01
  • 2021-05-11
  • 2020-10-11
  • 1970-01-01
  • 1970-01-01
  • 2017-01-31
  • 2015-10-07
  • 2020-11-28
相关资源
最近更新 更多