如何创建接受泛型的无状态功能组件?

IT技术 reactjs typescript
2021-05-26 19:19:18

Typescript 泛型可用于扩展接口。

interface Sample1<P> {
  prop1: P;
}

interface Sample2<P> extends Sample1<P> {
  prop2: string;
}

但是当我尝试创建一个使用通用接口的功能组件时,typescript会抛出错误。

const SampleSFC: React.SFC<Sample2<P>> = () => <div />;

错误 TS2304:找不到名称“P”。

如果我P用已知类型替换string错误就会消失。

const SampleSFC: React.SFC<Sample2<string>> = () => <div />;

而不是硬编码类型的P,即完全杀死仿制药的目的,我想启用的用户SampleSFC以类型的集合P

我怎样才能做到这一点?如果这是不可能的,那么我应该遵循什么样的替代设计,让我拥有带有通用props的 SFC。

3个回答

泛型类型变量可用于函数和类型声明,如接口Sample1Sample2. 但是当你实际使用/调用/调用泛型类型时,你必须指定一个具体的类型参数而不是P.

React.SFC- 无状态函数组件类型 - 是一个接口声明,因此在const赋值中使用它需要一个具体的类型P

我想到的唯一解决方案是让它SFC看起来像functionts-compiler类型,因为函数声明/表达式可以分配泛型类型参数。有关 一些示例,请参阅Typescript 文档

通用证监会声明

React.SFC在您的作业中省略类型注释const SampleSFC

const SampleSFC = <P extends {}>(props: Sample2<P>) => <div>{props.prop1} </div>;

注意:<P extends {}>由于与泛型的 JSX/TSX 兼容性问题,需要部分,请参阅本文末尾。

这样,您的组件仍然是一个普通的函数。如果您需要 中的其他成员React.SFC,请将它们添加到您的props中。例如,如果children需要:

const SampleSFC = <P extends {}>(props: Sample2<P> & { children?: ReactNode })
 => <div >{props.prop1} </div>;

通用 SFC 用法

Typescript 2.9 开始,可以直接在 JSX 中设置类型参数。

const MyApp: React.SFC<{}> = (props) => {
    return <SampleSFC<number> prop2="myString" prop1={1} /> 
}

备择方案

1.) 如果您不喜欢这些怪癖,切换到类组件会很容易。

class SampleSFCClass<P> extends React.Component<Sample2<P>> {
    render() {
        return <div>{this.props.prop1}</div>
    }
}

const MyApp2: React.SFC<{}> = (props) => {
    return <SampleSFCClass<number> prop2="myString" prop1={1} /> 
}

2.) 您甚至可以将 包装SFC在另一个函数中。

const withGeneric: <P>() => React.SFC<Sample2<P>> = <P extends {}>() => {
    return (props) => <div> {props.prop1}  {props.prop2} </div>;
}

const SampleSFCCalled: React.SFC<Sample2<string>> = withGeneric<string>();

const MyApp: React.SFC<{}> = (props) => {
        return <SampleSFCCalled prop2="myString" prop1="aString" />
}

它会起作用,但缺点可能是性能略有下降,因为该SFC功能总是在父组件的每个渲染周期中重新创建。

JSX/TSX 与泛型的兼容性问题:

在某些情况下,由于语法不明确,Typescript 在结合 JSX/TSX(直到最近的 3.0.1)解析泛型时似乎存在问题。编译错误将是:

“JSX 元素没有相应的结束标记。”

一位贡献者建议function在这种情况下使用语法(请参阅问题)。

当您坚持使用箭头函数时,解决方法是让类型参数从 object 扩展(显示在此处此处)以阐明它是通用 lambda 而不是 JSX 标记:

<P extends {}> 或者 <P extends object>

希望,这有帮助。

泛型只适用于泛型类型。因此,当您声明 type 时interface Sample1<P>,您可以P在该声明中将其用作类型。但是,具体类型Sample1<P>实际上并不存在。而是P在使用类型时Sample1类型的占位符

因此,使用Sample1<P>泛型类型定义,外面有一个泛型类型参数P并没有真正的工作。那么您只能使用实际的非泛型类型P

这就是为什么Sample1<string>作品,因为string是类型参数的有效值PSample1<P>定义。

如果你想要一个SampleSFC适用于任何类型的P,那么你可以将它创建为React.SFC<Sample1<any>>

const SampleSFC: React.SFC<Sample1<any>> = () => <div />;
interface MyComponentProps<T> {
  value?: T;
}

export const MyComponent = <T,>(
  props: React.PropsWithChildren<MyComponentProps<T>>,
) => {
  return (
    <View>
      {props.children}
      <Text>{(props.value as unknown) as string}</Text>
    </View>
  );
};

然后在渲染方法中..

return (
<MyComponent<string> />
)