reactjs - 使用子接口(interface)作为具有通用接口(interface)的 prop 接口(interface)

标签 reactjs typescript

我正在尝试在 React 中实现一些不断出现错误的类型。

我的想法是,我有一个枚举 (EBreakpoint),它与我们支持的每个设备绑定(bind)。代理包装器组件将每个设备作为 Prop ,并将值作为 Prop 解析给子组件。

TypeScript 部分有效,as I've demonstrated in a Typescript Playground ,但实现不断出现此错误:

JSX element type 'Element[] | IChild<any>' is not a constructor function for JSX elements.
  Type 'Element[]' is missing the following properties from type 'Element': type, props, key

Codesandbox 网址:https://codesandbox.io/s/serene-sea-3wmxk (代理的部分功能被移除,尽可能隔离问题)

index.tsx:

import * as React from "react";
import { render } from "react-dom";
import { Proxy } from "./Proxy";

import "./styles.css";

const ChildElement: React.FC<{ text: string }> = ({ text }) => {
  return <>{text}</>;
};

function App() {
  return (
    <div className="App">
      <Proxy Mobile={{ text: "Mobile" }}>
        <ChildElement text="Default" />
      </Proxy>
    </div>
  );
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);

Proxy.tsx:

import * as React from "react";

enum EBreakpoint {
  Mobile = "Mobile",
  Desktop = "Desktop"
}

interface IChild<P> extends React.ReactElement {
  props: P;
}

type TResponsiveProps<P> = { [key in EBreakpoint]?: P };

interface IProps<P> extends TResponsiveProps<P> {
  children: IChild<P>;
}

export function Proxy<P>({ children, ...breakpoints }: IProps<P>) {
  return Object.keys(breakpoints).length && React.isValidElement(children)
    ? Object.keys(breakpoints).map(breakpoint => (
        <div>{React.cloneElement(children, breakpoints[breakpoint])}</div>
      ))
    : children;
}

最佳答案

这不是您的代码或 JSX 中的错误(正如评论中所建议的那样)。这是 JSX 在 typescript 中的一个限制。

有了 JSX,你所有的代码都像

<Proxy Mobile={{ text: "Mobile" }}>
  <ChildElement text="Default" />
</Proxy>

将被编译为

React.createElement(Proxy, {
  Mobile: {
    text: "Mobile"
  }
}, React.createElement(ChildElement, {
  text: "Default"
}));

因此所有子组件都将作为 JSX.Element 传递给父组件。此接口(interface)是一个黑盒,无法检索像 prop 类型这样的属性:

The JSX result type

By default the result of a JSX expression is typed as any. You can customize the type by specifying the JSX.Element interface. However, it is not possible to retrieve type information about the element, attributes or children of the JSX from this interface. It is a black box.

目前有一个 issue为此开放,其中描述了如何推广 JSX.Element 以支持您的用例的路线图,但没有任何明确的时间表,何时以及在何种程度上或是否会落地。

然而,有一个快速的解决方法仍然具有您想要的功能,通过像这样专门声明您的代理的通用类型: 请注意,这不会执行类型检查,以确保 Proxy 和子项上的两个接口(interface)匹配。这是不可能的(如上所述)

enum EBreakpoint {
  Mobile = "Mobile",
  Desktop = "Desktop"
}

type TResponsiveProps<P> = { [key in EBreakpoint]?: P };

type IProps<P> = TResponsiveProps<P> & { children?: React.ReactNode };

export function Proxy<P>({
  children,
  ...breakpoints
}: IProps<P>): React.ReactElement | null {
  return Object.keys(breakpoints).length && React.isValidElement(children) ? (
    <React.Fragment>
      {Object.keys(breakpoints).map(breakpoint => (
        <div>{React.cloneElement(children, breakpoints[breakpoint])}</div>
      ))}
    </React.Fragment>
  ) : (
    <React.Fragment>{children}</React.Fragment>
  );
}

然后在你的 index.tsx 中像这样:

<Proxy<{ text: string }> Mobile={{ text: "Mobile" }}>
  <ChildElement text="Default" />
</Proxy>

当然你也可以将你的prop type { text: string } 提取到另一个接口(interface)中以供重用。

关于reactjs - 使用子接口(interface)作为具有通用接口(interface)的 prop 接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57586356/

相关文章:

javascript - 为什么在react.js文档( "tutorial"部分)中描述了拥有正在运行的服务器的必要性?

javascript - react 状态组件 - 无法读取未定义的属性 'state'

javascript - 如何在 compoentWillMount ReactJS 中使用 setState()

javascript - 为什么要使用 redux-thunk?

javascript - 在 React 中使用 map 创建组件

javascript - react : "Warning: Unknown DOM property itemscope. Did you mean itemScope?"

Typescript 路径在 Express 项目中不起作用

typescript - TS 找不到模块

javascript - javascript/typescript 中的代码减少/可重用性技术

Angular 6 类型错误 : Cannot read property 'e4b7...f' of undefined