reactjs - React TypeScript 使用组件的 props 声明接口(interface)

标签 reactjs typescript react-hooks

import IntroductionArea from "../components/IntroductionArea";

interface AppSection {
  url: string;
  component: React.FC<any> | React.LazyExoticComponent<React.FC<any>>;
  props?: React.ComponentProps<any>;
}

export const appSections: AppSection[] = [
  {
    url: "/home",
    component: IntroductionArea,
    props: {
      text: "Hello World",
      name: "Foo Bar"
    }
  },
];

你好。我有创建一个对象数组的代码,这些对象接受一个组件和该组件所需的 Prop 。下面的代码是有效的,但不是类型安全的,因为 AppSection 接口(interface)的 props 成员接受 any。我尝试用 typeof component 替换 any 但你不能在声明它的接口(interface)中引用 component

如何在保持相同功能的同时使此代码类型安全?

谢谢

最佳答案

您需要泛型来使一个值的类型依赖于另一个值类型。

在这种情况下,您需要为组件设置 props 类型。那么让我们从这里开始:

type Component = React.FC<any> | React.LazyExoticComponent<React.FC<any>>

interface AppSection<C extends Component> {
  url: string;
  component: C;
  props?: React.ComponentProps<C>;
}

现在测试一下:

const introSection: AppSection<typeof IntroductionArea> = {
    url: "/home",
    component: IntroductionArea,
    props: {
        text: "Hello World",
        name: "Foo Bar",
        noPropHere: false // Type '{ text: string; name: string; noPropHere: false; }' is not assignable to type '{ text: string; name: string; }'.
    }
}

Playground

很好,我们得到了我们排除的错误,这证明它正在工作。


制作一个数组要困难得多。您不能只执行 AppSection[],因为 AppSection 的通用参数是必需的。所以你可以这样做:

const introSection: AppSection<typeof IntroductionArea> = {
    url: "/home",
    component: IntroductionArea,
    props: {
        text: "Hello World",
        name: "Foo Bar",
        noPropHere: false // error as expected
    }
}

const otherSection: AppSection<typeof OtherArea> = {
    url: "/home",
    component: OtherArea,
    props: {
        other: "Hello World",
    }
}

export const appSections = [introSection, otherSection]

但是如果你尝试渲染这些,你会得到一个类型错误:

const renderedSections = appSections.map(item => {
    const SectionComponent = item.component
    return <SectionComponent {...item.props} />
    // Type '{} | { text: string; name: string; } | { other: string; }' is not assignable to type 'IntrinsicAttributes & { text: string; name: string; } & { other: string; }'.
    //  Type '{}' is missing the following properties from type '{ text: string; name: string; }': text, name(2322)
})

这意味着 typescript 不能保证数组中某个项目的 props 实际上与数组中该项目的组件匹配。


最后,React Router对于他们如何处理这个问题是一个很好的引用,因为这里的用例非常相似。它们允许您以两种方式声明路由呈现的内容:

<Route path="/home"><Home /></Route>
<Route path="/other" render={() => <Other />} />

请注意,在这两种情况下,您都是自己渲染组件并传递结果。这样组件就可以只验证它自己的 props 而不需要担心所有这些细节。正如您从上面看到的那样,这让事情变得很多更容易。

在你的情况下可能看起来像:

interface AppSection {
  url: string;
  render(): React.ReactNode;
}

function IntroductionArea(props: {text: string, name: string}) {
    return null
}

function OtherArea(props: {other: string}) {
    return null
}

export const appSections: AppSection[] = [
  { url: '/home', render: () => <IntroductionArea text="Hello, World!" name="Foo Bar"/> },
  { url: '/other', render: () => <OtherArea other="Other text" /> },
];

// Render all sections
const renderedSections = appSections.map(section => section.render())

Playground

关于reactjs - React TypeScript 使用组件的 props 声明接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68945172/

相关文章:

javascript - 为什么useDispatch会重新渲染父组件?

reactjs - react Hook 和 typescript - 类型 '***' 上不存在属性 'never'

javascript - React useState 钩子(Hook)没有持续更新

javascript - Prop 仅在我刷新页面后呈现

javascript - React - 在 DOM 中多次使用完全相同的组件实例

javascript - 使用 Basic Auth 在 ReactJS 中获取返回 401(未授权)。预检请求未通过访问控制检查

typescript - 如何在 TypeScript 3+ 中正确输入通用元组剩余参数?

javascript - Promise.all 返回 promise 数组的 promise ,但返回单个 promise 的正确值

javascript - 在 Typescript/Angular 中,instanceof 函数参数返回 false

angularjs - 将 Angular 2 Web 应用程序代码转换为 ionic 2 移动应用程序代码?