在道具之间使用泛型作为反应组件

时间:2019-07-25 18:56:43

标签: reactjs typescript

我正在尝试键入以下组件:

type Props<Data> = {
  data: Data[]
  render: ({ value }: { value: Data }) => React.ReactElement
}

const List: React.FC<Props> = ({data, render}) => {…}

我遇到的问题是,我希望Data是通用的,无论您在数组中传入什么内容,我都想强烈键入渲染以使value知道为Data类型。问题是我不确定如何使用它。理想情况下,我想执行以下操作:

<List data={[1,2,3]} render={(value) => <div>{value + 2}</div>}/>

并完成操作,在这种情况下render将知道value是一个数字。这可能吗?

2 个答案:

答案 0 :(得分:0)

如果您尝试传递数字数组(从data={1,2,3}推算得出)。那么您只需要将数字作为通用类型传递给您的Props。您需要做更多的映射才能使类型正确匹配。

Functional Component Way

export interface ListProps<T = unknown> {
  data: T[];
  render: (item: T) => React.ReactElement;
}

export const GenericListItems = <T extends any>(): React.FC<ListProps<T>> => ({
  data,
  render
}): any => {
  return data.map(render);
};

严格输入您的列表的样子

const StringList = GenericListItems<string>();
const NumberList = GenericListItems<number>();

Component Class Way

interface JsxClass<P, S> extends React.Component<P, S> {
  render(): React.ReactElement<P>[];
}

interface GenericComponentType<P, S> {
  new (props: P): JsxClass<P, S>;
}

interface ListProps<T> {
  data: T[];
  render: (value: T) => any;
}

class List<T> extends React.Component<ListProps<T>, {}> {
  render(): React.ReactElement<any>[] {
    return this.props.data.map(this.props.render);
  }
}

严格输入您的列表的样子

const NumberList: GenericComponentType<ListProps<number>, {}> = List;
const StringList: GenericComponentType<ListProps<string>, {}> = List;

这两个(组件类或功能组件)实例都可以以相同的方式使用。

<NumberList
  data={[1, 2, 3]}
  render={value => <div>{value + 2}</div>}
/>
<StringList
  data={["I", "Render", "Strings"]}
  render={value => <div>{value}</div>}
/>

答案 1 :(得分:0)

如果您需要Data作为通用参数,则可以在组件定义中声明它。 所以像class List<Data> extends React.Component<ListProps<Data>>function List<Data>(props: ListProps<Data>)之类的东西,让打字稿完成其余的工作。

这是一个完整的代码示例:

import React from "react";

interface ListProps<Data> {
  data: Data[];
  render: (item: Data) => React.ReactNode;
}

function List<Data>({ data, render }: ListProps<Data>) {
  return <div>{data.map(render)}</div>;
}

const element1 = <List data={[1.234, 2.345, 3.456]} render={v => <span>{v.toFixed(2)}</span>} />;

const element2 = (
  <List data={["abc", "bca", "cab"]} render={v => <span>{v.substring(0, 1)}</span>} />
);

编辑

要强制数据为特定类型,可以将通用参数传递给List,如下所示:

const element1 = (
  <List<number>
    // Typescript will complain:
    // Type 'string' is not assignable to type 'number'.ts(2322)
    data={["1.234", 2.345, 3.456]}
    render={v => <span>{v.toFixed(2)}</span>}
  />
);

在最后一个示例中,如果删除通用参数,则会收到另一个错误,因为数据将被推断为number | string

const element1 = (
  <List
    data={["1.234", 2.345, 3.456]}
    // Typescript will complain:
    //Property 'toFixed' does not exist on type 'ReactText'.
    //  Property 'toFixed' does not exist on type 'string'.ts(2339)
    // and `v` is infered to be (parameter) v: string | number
    render={v => <span>{v.toFixed(2)}</span>}
  />
);