繁体   English   中英

通用无状态组件 React 的类型? 或在 typescript 中扩展通用 function 接口以具有进一步的通用性?

[英]Type of Generic Stateless Component React? OR Extending generic function interface in typescript to have a further generic?

问题Stateless Functional Component的接口给出为

interface SFC<P = {}> {
    (props: P & { children?: ReactNode }, context?: any): ReactElement<any> | null;
    propTypes?: ValidationMap<P>;
}

我的组件的 prop 类型也是通用的:

interface Prop<V>{
    num: V;
}

如何正确定义我的组件? 作为:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>

character 27处出现错误, Cannot find name 'T'

这里是: Typescript Playground of modified example

我的发现

1:Typescript 2.9.1 支持有状态通用组件: http://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-9.html#generic-type-arguments-in-jsx-elements

class myCom<T> extends React.Component<Prop<T>, any> {
   render() {
      return <div>test</div>;
   }
}

2:扩展SFC以创建以下答案中提到的新接口将使组件的 prop 类型为anyTypescript React stateless function 与我不想要的通用参数/返回类型 我想为我的道具提供正确的类型

你不能像这样使用泛型:

const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>

TypeScript 规范指出:

形式的构造

< T > ( ... ) => { ... }

可以解析为带有类型参数的箭头函数表达式或应用于没有类型参数的箭头函数的类型断言。

来源; 微软/TypeScript 规范.md

您的声明与 TypeScript 规范中定义的模式不匹配,因此它不起作用。

但是,您可以不使用 SFC 接口而仅自行声明。

interface Prop<V> {
    num: V;
}

// normal function
function Abc<T extends string | number>(props: Prop<T>): React.ReactElement<Prop<T>> {
    return <div />;
}

// const lambda function
const Abc: <T extends string | number>(p: Prop<T>) => React.ReactElement<Prop<T>> = (props) => {
   return <div />
};

export default function App() {
    return (
        <React.Fragment>
            <Abc<number> num={1} />
            <Abc<string> num="abc" />
            <Abc<string> num={1} /> // string expected but was number
        </React.Fragment>
    );
}

有一种模式可以通过在组件外部声明通用组件类型别名,然后在需要时简单地断言它来缓解此问题。

不那么漂亮,但仍然可以重用和严格。

interface IMyComponentProps<T> {
  name: string
  type: T
}

// instead of inline with component assignment
type MyComponentI<T = any> = React.FC<IMyComponentProps<T>>

const MyComponent: MyComponentI = props => <p {...props}>Hello</p>

const TypedComponent = MyComponent as MyComponentI<number>

工厂模式:

import React, { SFC } from 'react';

export interface GridProps<T = unknown> {
  data: T[];
  renderItem: (props: { item: T }) => React.ReactChild;
}

export const GridFactory = <T extends any>(): SFC<GridProps<T>> => () => {
  return (
    <div>
      ...
    </div>
  );
};

const Grid = GridFactory<string>();

更新 08/03/2021

为了避免钩子规则错误,你必须像这样调整语法:

import React, { FC } from 'react';

export interface GridProps<T = unknown> {
  data: T[]
  renderItem: (props: { item: T }) => React.ReactChild
}

export const GridFactory = <T extends any>() => {
  const Instance: FC<GridProps<T>> = (props) => {
    const [state, setState] = useState(props.data)

    return <div>...</div>
  }

  return Instance
}

const Grid = GridFactory<string>()

我提出了一个类似但略有不同的解决方案(与朋友集思广益)。 我们试图创建一个 Formik 包装器,并设法使其以下列方式工作:

import React, { memo } from 'react';

export type FormDefaultProps<T> = {
  initialValues: T;
  onSubmit<T>(values: T, actions: FormikActions<T>): void;
  validationSchema?: object;
};

// We extract React.PropsWithChildren from React.FunctionComponent or React.FC
function component<T>(props: React.PropsWithChildren<FormDefaultProps<T>>) {
  // Do whatever you want with the props.
  return(<div>{props.children}</div>
}

// the casting here is key. You can use as typeof component to 
// create the typing automatically with the generic included..
export const FormDefault = memo(component) as typeof component;

然后,您可以像这样使用它:

 <FormDefault<PlanningCreateValues>
        onSubmit={handleSubmit}
        initialValues={PlanningCreateDefaultValues}
      >
         {/*Or any other child content in here */}
        {pages[page]}
</FormDefault>

我无法通过方法表达式实现这一点:

const a: React.FC<MyProp> = (prop) => (<>MyComponent</>);

我有办法,但我不确定是否完美

interface ComponentProps<T> {
    text: T;
}

export const Component= <T,>(props: ComponentProps<T>) => {
  const { text } = props
  const [s] = useState(0) // you can use hook
  return (<div>yes</div>)
}

你可以像这样使用组件:

 (
  <>
    <Component<string> text="some text" />
  </>
 )

@chris 在这里展示的工厂模式很棒,但我不能将 React Hooks 与它一起使用。 所以我在用这个。

// Props
interface Props<T> {
  a: T;
}

// Component
export const MyComponent: <T>(p: PropsWithChildren<Props<T>>) => React.ReactElement = props => {
  return <div>Hello Typescript</div>;
};

如果您不需要孩子,您可以删除 PropsWithChildren 部分。 道具分解和钩子也能工作。

export const MyComponent: <T>(p: Props<T>) => React.ReactElement = ({ a }) => {
  const [myState, setMyState] = useState(false);
  return <div>Hello Typescript</div>;
};

你有这个:

interface Prop<V> {
    num: V;
}

您的组件定义如下:

const myCom: SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>

这是行不通的,因为您需要在接口中为V提供具体的类型,因为您正在组件中实现它。

看起来像这样:

const myCom: SFC<Prop<object>> = <T>(props: Prop<T>)=> <div>test</div>

注意我使用object地方T 这只是一个例子。

根据 jmattheis 的帖子的通用无状态组件示例。

MyGenericStatelessComponent.tsx

import React from "react";

type Prop<T> = {
    example: T;
};

const MyGenericStatelessComponent: <T extends Record<string, number | string>>(props: Prop<T>) => JSX.Element = <
    T extends Record<string, unknown>
>(
    props: Prop<T>
): JSX.Element => {
    return (
        <div>
            Example Prop id: {props.example.id}, Example Prop name: {props.example.name}
        </div>
    );
};

export default MyGenericStatelessComponent;

用法:

<MyGenericStatelessComponent example={{ id: 1, name: "test01" }} />

使用T = any作为 @vadistic 示例有效,但您不会进行任何类型检查。 使用此代码,您将进行代码完成和类型检查。

interface IProps<TModel> extends RouteComponentProps {
    headerText?: string | React.ReactNode;
    collection: TModel[];
}

interface ICommonSortableType extends ISortableItem {
    id: number;
    isCorrectResponse: boolean;
}

interface ISortableItem {
    sortableId: number;
}    

type GenericFunctionalComponent<TModel> = React.FC<IProps<TModel>>;
const CommonSortableList: GenericFunctionalComponent<ICommonSortableType> = (props) => {
...
}

然后可以像这样使用:

class CommonSortableType {
    public sortableId: number = -1;
    public id: number = -1;
    public isCorrectResponse: boolean = false;
}

<CommonSortableList
    collection={item.commonSortableTypes} //Is CommonSortableType[]
    headerText={<FormattedMessage id="item.list" />}
</CommonSortableList>

class ExtendedOptionDto extends OptionDto implements ICommonSortableType {
    public sortableId: number = -1;
}

class OptionDto {
    public id: number = -1;
    public isCorrectResponse: boolean = false;
}

<CommonSortableList
    collection={item.extendedOptionDtos} //Is ExtendedOptionDto[]
    headerText={<FormattedMessage id="item.list" />}
</CommonSortableList>

如果你想在类型定义中包含 React 的 FunctionComponent 类型(例如,让返回类型自动进行类型检查),如果你不介意重复这些类型,你可以使用这样的构造:

interface MyProps<T> {
  val: T
}

const Component: React.FC<MyProps<any>> = <T>({
  val
}: MyProps<T>) => {
  // Your code and hooks here
  return <div>Heya</div>
}

const Component的类型是React.FC<MyProps<any>> ,这意味着您可以将它与MyProps类型参数T的任何有效值一起使用,但是由于实现在实现代码中重新指定了泛型类型参数T它的类型是T而不是any ,因此它将被正确地进行类型检查。 T可能有限制说明符,例如extends string ,您只需要在接口规范和 function 实现签名中重复它们。

您可以放弃使用React.FC进行注释,只需编写:

const myCom = <T>(props: Prop<T>) => <div>test</div>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM