TypeScript React HOC 返回类型与装饰器兼容
Posted
技术标签:
【中文标题】TypeScript React HOC 返回类型与装饰器兼容【英文标题】:TypeScript React HOC return type compatible with decorator 【发布时间】:2019-04-30 04:13:50 【问题描述】:我正在使用带有 TypeScript 的 React Native。我写了一个 HOC,我喜欢用它作为装饰器来给组件一个徽章:
import React, Component, ComponentClass, ReactNode from "react";
import Badge, BadgeProps from "../Badge";
function withBadge<P>(
value: number,
hidden: boolean = value === 0
): (WrappedComponent: ComponentClass<P>) => ReactNode
return (WrappedComponent: ComponentClass<P>) =>
class BadgedComponent extends Component<P>
render()
return (
<React.Fragment>
<WrappedComponent ...this.props />
!hidden && <Badge value=value />
</React.Fragment>
);
;
export default withBadge;
现在的问题是,当我尝试将此组件用作这样的装饰器时:
import React, PureComponent from "react";
import Icon from "react-native-elements";
import getIconName from "../../services/core";
import withBadge from "../WithBadge/withBadge";
import styles from "./styles";
@withBadge(1)
export default class BadgedCart extends PureComponent
render()
return (
<Icon
type="ionicon"
name=getIconName("cart")
containerStyle=styles.iconRight
onPress=() =>
// Nothing.
/>
);
我得到错误:
[ts]
Unable to resolve signature of class decorator when called as an expression.
Type 'null' is not assignable to type 'void | typeof BadgedCart'. [1238]
我尝试过其他不同的返回类型,例如 JSX.Element
或 ReactElement<any>
,但唯一有效的只有 any
,它违背了 TypeScript 的目的。高阶组件应该有什么返回类型?
编辑:当我将返回类型(如建议的Praveen)更改为typeof PureComponent
时,@withBadge(1)
的错误更改为:
[ts]
Unable to resolve signature of class decorator when called as an expression.
Type 'typeof PureComponent' is not assignable to type 'typeof BadgedOrders'.
Type 'PureComponent<any, any, any>' is not assignable to type 'BadgedOrders'.
Types of property 'render' are incompatible.
Type '() => ReactNode' is not assignable to type '() => Element'.
Type 'ReactNode' is not assignable to type 'Element'.
Type 'undefined' is not assignable to type 'Element'. [1238]
如果我尝试将其更改为 PureComponent
class BadgedComponent extends Component<P>
render()
return (
<React.Fragment>
<WrappedComponent ...this.props />
!hidden && <Badge value=value />
</React.Fragment>
);
;
抛出错误:
[ts]
Type '(WrappedComponent: ComponentClass<P, any>) => typeof BadgedComponent' is not assignable to type '(WrappedComponent: ComponentClass<P, any>) => PureComponent<, , any>'.
Type 'typeof BadgedComponent' is not assignable to type 'PureComponent<, , any>'.
Property 'context' is missing in type 'typeof BadgedComponent'. [2322]
【问题讨论】:
【参考方案1】:第一个问题是P
不在可以推断的位置。因为它在withBadge
上,并且该调用不会包含它。
第二个问题是类装饰器必须返回void
或与输入类相同的类型:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
这意味着装饰器签名不能是(WrappedComponent: ComponentClass<P>) => ReactNode
,因为它返回ReactNode
。它甚至不能是(WrappedComponent: ComponentClass<P>) => ComponentClass<P>
,因为它不会返回与传入的完全相同的值。一个解决方案是对编译器撒谎,并在我们实际返回一个新类时声明我们返回void
。这在运行时不会产生太大影响:
class Badge extends React.Component< value: number >
function withBadge(
value: number,
hidden: boolean = value === 0
): <P extends object>(WrappedComponent: ComponentClass<P>) => void
return <P extends object>(WrappedComponent: ComponentClass<P>) =>
class BadgedComponent extends Component<P>
render()
return (
<React.Fragment>
<WrappedComponent ...this.props />
!hidden && <Badge value=value />
</React.Fragment>
);
;
@withBadge(1)
export default class BadgedCart extends PureComponent
render()
return (
<div />
);
您可能会考虑放弃装饰器方法,转而使用简单的函数调用:
class Badge extends React.Component< value: number >
function withBadge(
value: number,
hidden: boolean = value === 0
): <P extends object>(WrappedComponent: ComponentClass<P>) => ComponentClass<P>
return <P extends object>(WrappedComponent: ComponentClass<P>) =>
class BadgedComponent extends Component<P>
render()
return (
<React.Fragment>
<WrappedComponent ...this.props />
!hidden && <Badge value=value />
</React.Fragment>
);
;
const BadgedCart = withBadge(1)(class extends PureComponent
render()
return (
<div />
);
);
export default BadgedCart;
【讨论】:
你可以在 BadgedCart 类中添加一个函数,会报错。以上是关于TypeScript React HOC 返回类型与装饰器兼容的主要内容,如果未能解决你的问题,请参考以下文章
带有 React.createContext 的 Typescript HOC
如何在 TypeScript 中为 React Apollo Query 组件编写 HOC?
React TypeScript HoC - 将组件作为道具传递