TypeScript React HOC return 类型与装饰器兼容
TypeScript React HOC return type compatible with decorator
我正在使用带有 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]
我尝试了其他 return 类型,例如 JSX.Element
或 ReactElement<any>
,但唯一有效的是 any
,这违背了 TypeScript 的目的。高阶组件应该具有什么 return 类型?
编辑: 当我将 return 类型(如 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]
第一个问题是P
不在可以推断的位置。由于它在 withBadge
上并且该调用将不包含它。
第二个问题是 class 装饰器必须 return void
或与输入相同的类型 class:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
这意味着装饰器签名不能是 (WrappedComponent: ComponentClass<P>) => ReactNode
,因为它 return 是 ReactNode
。它甚至不能是 (WrappedComponent: ComponentClass<P>) => ComponentClass<P>
,因为它与传入的 return 不完全相同。一个解决方案是对编译器撒谎,并声明我们 return void
当我们实际上 return 一个新的 class。这在运行时不会产生太大影响:
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;
我正在使用带有 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]
我尝试了其他 return 类型,例如 JSX.Element
或 ReactElement<any>
,但唯一有效的是 any
,这违背了 TypeScript 的目的。高阶组件应该具有什么 return 类型?
编辑: 当我将 return 类型(如 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]
第一个问题是P
不在可以推断的位置。由于它在 withBadge
上并且该调用将不包含它。
第二个问题是 class 装饰器必须 return void
或与输入相同的类型 class:
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
这意味着装饰器签名不能是 (WrappedComponent: ComponentClass<P>) => ReactNode
,因为它 return 是 ReactNode
。它甚至不能是 (WrappedComponent: ComponentClass<P>) => ComponentClass<P>
,因为它与传入的 return 不完全相同。一个解决方案是对编译器撒谎,并声明我们 return void
当我们实际上 return 一个新的 class。这在运行时不会产生太大影响:
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;