通用无状态组件 React 的类型?或在打字稿中扩展通用函数接口以具有进一步的通用性?
Posted
技术标签:
【中文标题】通用无状态组件 React 的类型?或在打字稿中扩展通用函数接口以具有进一步的通用性?【英文标题】:Type of Generic Stateless Component React? OR Extending generic function interface in typescript to have a further generic? 【发布时间】:2018-12-29 19:04:20 【问题描述】:问题: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
以创建一个新接口,如以下答案中所述,将使组件的道具类型为any
:
Typescript React stateless function with generic parameter/return types 我不想要。我想为我的道具提供正确的类型
【问题讨论】:
如果将通用的<T>
替换为<T extends >
会发生什么?
同样的错误。正如我想要的那样:const myCom: <T>SFC<Prop<T>>
我在描述@JonasW 中添加了游乐场链接。
@NamanKheterpal 我遇到了同样的问题并为此调查了一些时间。你可以找到我的解决方案,如何声明以及如何使用 React SFC here
【参考方案1】:
你不能像这样使用泛型:
const myCom: <T>SFC<Prop<T>> = <T>(props: Prop<T>)=> <div>test</div>
TypeScript 规范指出:
表单的构造
< T > ( ... ) => ...
可以解析为带有类型参数的箭头函数表达式或应用于不带类型参数的箭头函数的类型断言。
source; Microsoft/TypeScript spec.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>
);
【讨论】:
如果您与React.FC
有交集类型,是否可以以某种方式使用此模式?例如,我希望我的组件能够接受类型参数但也有复合组件。目前我有 const lambda fn 组件const Table = React.FC<TableProps> & Row: React.ComponentType<RowProps>
并且我希望在使用这个组件时能够使用类型参数。【参考方案2】:
有一种模式可以缓解此问题,方法是在组件之外声明通用组件类型别名,然后在需要时简单地声明它。
没有那么漂亮,但仍然可重复使用且严格。
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>
【讨论】:
这可能不符合泛型的目的。开发人员可能需要重写每个组件的功能并且不能有共同的行为。const MyComponent: MyComponentI = props => <p ...props>Hello</p>
每一个组件都会重复这一行。【参考方案3】:
工厂模式:
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>();
2021 年 8 月 3 日更新
为了避免钩子错误规则,您必须像这样巧妙地使用语法:
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>()
【讨论】:
<T extends any>
是不是用来避免TS把它当作类型断言来对待?聪明的主意。
这是正确的。如果需要,它也可以在这里扩展另一种类型【参考方案4】:
我提出了一个类似但略有不同的解决方案(与朋友集思广益)。我们试图创建一个 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</>);
【讨论】:
我走了你的路,onSubmit
中的values
参数总是未知的。你知道我哪里出错了吗?【参考方案5】:
@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>;
;
【讨论】:
【参考方案6】:根据 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" />
【讨论】:
您的示例只是一个返回元素的函数。此外,OP 用从 TS 2.9.1 开始的首选方式更新了他的问题【参考方案7】:使用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>
【讨论】:
【参考方案8】:我有办法,但我不确定是否完美
interface CategoryRelationProps<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" />
</>
)
【讨论】:
以上是关于通用无状态组件 React 的类型?或在打字稿中扩展通用函数接口以具有进一步的通用性?的主要内容,如果未能解决你的问题,请参考以下文章