React TS:我可以在不进行类型转换的情况下做到这一点吗? “可以用可能不相关的任意类型实例化”

Posted

技术标签:

【中文标题】React TS:我可以在不进行类型转换的情况下做到这一点吗? “可以用可能不相关的任意类型实例化”【英文标题】:React TS: Can I do this without typecasting? "could be instantiated with an arbitrary type which could be unrelated" 【发布时间】: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 而不是ReactNode。 tsplay.dev/WKkpzW我看看能不能更好的控制推理。 【参考方案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;注解。看起来它自己推断得很好。

以上是关于React TS:我可以在不进行类型转换的情况下做到这一点吗? “可以用可能不相关的任意类型实例化”的主要内容,如果未能解决你的问题,请参考以下文章

使用简单的管道限制为 2 位小数

TypeScript:我可以在不编写 index.ts 文件的情况下导入文件夹吗?

React 将 JSX 应用程序转换为 Typescript

为啥我可以键入别名函数并在不强制转换的情况下使用它们?

怎么在react里写ts

如何在前端(TS、React)上将 CSV 转换并下载到 XLSX