从父props推断类型

IT技术 reactjs typescript
2022-07-27 00:36:50

有没有办法让我的子组件知道父组件的props类型?

我不想将它们硬编码到孩子中,因为孩子将被多个父母使用,props略有不同。

一个简化的例子:

// Parent.tsx

import React, {ReactElement} from "react";

interface Row {
  id: string;
  name: string;
  tags: string[];
}

const Parent = (): ReactElement => {
  const rows: Row[] = [
    {
      id: "1",
      name: "Foo",
      tags: ["new"],
    },
    {
      id: "1",
      name: "Bar",
      tags: ["new", "sale"],
    },
  ];

  const formatRow = (row: Row): ReactElement => (
    <tr key={row.id}>
      <td>{row.id}</td>
      <td>{row.name}</td>
      <td>{row.tags.map(tag => <strong>{tag}</strong>)}</td>
    </tr>
  );

  return <Child formatRow={formatRow} rows={rows} />;
};

export default Parent;
// Child.tsx

import React, {ReactElement} from "react";

const Child = ({formatRow, rows}): ReactElement => {
  const columnCount = Object.keys(rows[0]).length;

  return (
    <table>
      <tbody>
        {rows.map(row => formatRow(row))}
      </tbody>
      <tfoot>
        <tr>
          <td colSpan={columnCount}>Footer</td>
        </tr>
      </tfoot>
    </table>
  );
};

export default Child;

然后也可能有另一个父级,Parent2具有这种形状,用于它自己的实现Row

// Parent2.tsx

interface Row {
  id: string;
  count: number;
}

const Parent2 = (): ReactElement => {
  // similar code to `Parent` here
};

export default Parent2;

我不确定如何让Child组件知道formatRow期望 aRow并返回 a ReactElement,并且 rows 是一个Row对象数组?

我认为泛型可能是答案,但我对此知之甚少,因此无论是泛型还是其他方面的任何帮助都会受到赞赏。

1个回答

通常,如果你想成为argument agnostic你应该使用泛型:


import React, { ReactElement } from "react";

interface Row {
  id: string;
  name: string;
  tags: string[];
}

const Child = <R,>({ formatRow, rows }: { rows: R[], formatRow: (row: R) => ReactElement }): ReactElement => {
  const columnCount = Object.keys(rows[0]).length;

  return (
    <table>
      <tbody>
        {rows.map(row => formatRow(row))}
      </tbody>
      <tfoot>
        <tr>
          <td colSpan={columnCount}>Footer</td>
        </tr>
      </tfoot>
    </table>
  );
};

const Parent = (): ReactElement => {
  const rows: Row[] = [
    {
      id: "1",
      name: "Foo",
      tags: ["new"],
    },
    {
      id: "1",
      name: "Bar",
      tags: ["new", "sale"],
    },
  ];

  const formatRow = (row: Row): ReactElement => (
    <tr key={row.id}>
      <td>{row.id}</td>
      <td>{row.name}</td>
      <td>{row.tags.map(tag => <strong>{tag}</strong>)}</td>
    </tr>
  );

  return <Child formatRow={formatRow} rows={rows} />;
};

操场

在这里,您有简化版本:

interface ChildProps<R> {
  rows: R[],
  formatRow: (row: R) => ReactElement
}

const Child = <R,>({ formatRow, rows }: ChildProps<R>): ReactElement => {
  const columnCount = Object.keys(rows[0]).length;

  return (
    <table>
      <tbody>
        {rows.map(row => formatRow(row))}
      </tbody>
      <tfoot>
        <tr>
          <td colSpan={columnCount}>Footer</td>
        </tr>
      </tfoot>
    </table>
  );
};

因此,R是推断的rows属性类型。这就是推理的工作原理。您提供任何值rows并将R表示值的类型。然后,您可以R用于其他属性。

如果您对函数参数的类型推断感兴趣,可以查看我的文章