我目前正在将 React 应用程序迁移到 Typescript,而我唯一缺少的是嵌套 HOC 具有正确类型的传入和传出道具。
拥有一个 HOC 并放入一个组件可以正常工作,但是一旦我想嵌套这些,我就会从 Typescript 编译器中得到错误。
我试图通过创建一个相当简单的示例来理解它(因为在我们的代码库中它有很多嵌套的 HOC),但我仍然缺少嵌套 HOC 的正确类型,而且我不明白应该如何完成它。
假设我有两个 HOC(都有一个需要在包装组件上设置的道具,以及一个将添加到包装组件的道具):
// This HOC adds an "imprint" prop to the wrapped component and takes the company name in
type WithImprintText = {
imprint: string;
};
type CompanyProps = {
companyName: string;
};
function withImprint<TProps extends CompanyProps>(
WrappedComponent: React.ComponentType<
Omit<TProps, keyof CompanyProps> & WithImprintText
>
)
: React.ComponentType<CompanyProps & Omit<TProps, keyof WithImprintText>> {
return class extends React.Component<
CompanyProps & Omit<TProps, keyof WithImprintText>
> {
render() {
return (
<WrappedComponent
{...(this.props as TProps)}
imprint={"Imprint by: " + this.props.companyName}
/>
);
}
};
}
// This HOC adds an "title" prop to the WrappedComponent and takes a version prop
type WithTitleText = {
title: string;
};
type VersionProps = {
version: string;
};
function withTitle<TProps extends VersionProps>(
WrappedComponent: React.ComponentType<
Omit<TProps, keyof VersionProps> & WithTitleText
>
) {
return class extends React.Component<VersionProps & Omit<TProps, keyof WithTitleText>
> {
render() {
return (
<WrappedComponent
{...(this.props as TProps)}
title={"Current version is: " + this.props.version}
/>
);
}
};
}
这是包装好的组件,包括嵌套的 HOC:
// This component will be wrapped in both HOCs
type WrappedProps = {
desc: string;
};
function mainComponent(
s: WrappedProps & WithTitleText & WithImprintText
) {
return (
<div>
<h1>{s.title}</h1>
<h2>{s.desc}</h2>
<footer>{s.imprint}</footer>
</div>
);
}
type WithTitleAndImprintProps = {} & VersionProps & CompanyProps;
// This HOC wraps both other HOCs
// TODO: missing correct type information
const withTitleAndImprint = <TProps extends WithTitleAndImprintProps>(
WrappedComponent: React.ComponentType<TProps & WithTitleText & WithImprintText
>
) => {
return withTitle<TProps>(withImprint<TProps>(WrappedComponent));
};
//
const WrappedMainComponent = withTitleAndImprint<WrappedProps & CompanyProps & VersionProps>(mainComponent);
带有return withTitle<TProps>(withImprint<TProps>(WrappedComponent));
显示错误的行:
Argument of type 'ComponentType<CompanyProps & Omit<TProps, "imprint">>' is not assignable to parameter of type 'ComponentType<Omit<TProps, "version"> & WithTitleText>'.
Type 'ComponentClass<CompanyProps & Omit<TProps, "imprint">, any>' is not assignable to type 'ComponentType<Omit<TProps, "version"> & WithTitleText>'.
Type 'ComponentClass<CompanyProps & Omit<TProps, "imprint">, any>' is not assignable to type 'ComponentClass<Omit<TProps, "version"> & WithTitleText, any>'.
Types of property 'propTypes' are incompatible.
Type 'WeakValidationMap<CompanyProps & Omit<TProps, "imprint">> | undefined' is not assignable to type 'WeakValidationMap<Omit<TProps, "version"> & WithTitleText> | undefined'.
Type 'WeakValidationMap<CompanyProps & Omit<TProps, "imprint">>' is not assignable to type 'WeakValidationMap<Omit<TProps, "version"> & WithTitleText>'.
Type '"title" | Exclude<keyof TProps, "version">' is not assignable to type '"companyName" | Exclude<keyof TProps, "imprint">'.
Type '"title"' is not assignable to type '"companyName" | Exclude<keyof TProps, "imprint">'
我尝试了几种使用泛型的组合,但我没有找到解决方案。不用说,代码本身可以找到,并且在使用时一切都正确呈现:
export default function App() {
return (
<div className="App">
<div>
<WrappedMainComponent version="1" desc="some desc" companyName="Demo Ltd." />
</div>
</div>
);
}
我使用上面的代码创建了一个带有工作示例的沙箱:https ://codesandbox.io/s/shy-sun-3g191