【发布时间】:2019-03-02 16:03:06
【问题描述】:
简介
我正在尝试为React Native 中的Dialog 组件创建一个Higher Order Component。可悲的是,我有一些我根本不理解的编译错误。我在Higher Order Components 和TypeScript 上关注了this tutorial,但它没有显示让ref 工作的示例。
设置
我有一个名为DialogLoading 的组件,我通过一个名为withActions 的Higher Order Component 导出它。 withActionscomponent 定义了两个接口,用于确定它注入了哪些 props 以及它接受了哪些额外的 props。在下面的代码中,类型参数 C、A 和 P 代表 ComponentType、@987654337 @ 和 PropType 分别。
接口是:
interface InjectedProps<A>
{ onActionClicked: (action: A) => void;}
和
interface ExternalProps<C, A>
{ onActionClickListener?: (component: C | null, action: A) => void;}
我还声明了一个类型别名,表示HOC 的最终道具类型。这个类型应该有被包装组件的所有props,ExternalProps<C, A>接口的所有props,但没有InjectedProps<A>接口的props。声明如下:
type Omit<T, K> = Pick<T, Exclude<keyof T, K>>;
type Subtract<T, K> = Omit<T, keyof K>;
type HocProps<C, A, P extends InjectedProps<A>> = Subtract<P, InjectedProps<A>> & ExternalProps<C, A>;
Higher Order Component 然后声明如下:
export default <C, A, P extends InjectedProps<A>> (WrappedComponent: React.ComponentType<P>) =>
{
const hoc = class WithActions extends React.Component<HocProps<C, A, P>>
{
...Contents of class removed for breivity.
private onActionClicked = (action: A) =>
{
this.onActionClickedListeners.forEach(listener =>
{ listener(this.wrapped, action);});
}
private wrapped: C | null;
render()
{
return (
<WrappedComponent ref={i => this.wrapped = i} onActionClicked={this.onActionClicked} {...this.props} />
);
}
}
return hoc;
}
并且可以用于:
<DialogLoading onActionClickListener={this.onActionClickListener} title="Loading Data" section="Connecting" />;
问题
在 HOC 的渲染函数内的 ref 回调上,TypeScript 给了我以下错误消息:
[ts] Property 'ref' does not exist on type 'IntrinsicAttributes & InjectedProps<A> & { children?: ReactNode; }'.
[ts] Type 'Component<P, ComponentState, never> | null' is not assignable to type 'C | null'.
Type 'Component<P, ComponentState, never>' is not assignable to type 'C'.
我怀疑这是因为传入的WrappedComponent 是React.ComponentType<P> 类型,它是React.ComponentClass<P> 和React.SFC<P> 的联合类型。抛出错误,因为 React 中的无状态组件不接受 ref 回调。一个可能的解决方案是将其类型更改为 just React.ComponentClass<P>。
这种种解决了问题,但奇怪的是,包装组件的onActionClicked 属性现在抛出了一个新错误!错误是:
[ts] Type '(action: A) => void' is not assignable to type '(IntrinsicAttributes & IntrinsicClassAttributes<Component<P, ComponentState, never>> & Readonly<{ children?: ReactNode; }> & Readonly<P>)["onActionClicked"]'.
WithActions.tsx(7, 5): The expected type comes from property 'onActionClicked' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Component<P, ComponentState, never>> & Readonly<{ children?: ReactNode; }> & Readonly<P>'
第二个错误让我完全困惑。使它更加陌生的是,当我将HocProps 的类型别名调整为以下(即我不再从P 中减去InjectedProps<A>)时:
type HocProps<C, A, P extends InjectedProps<A>> = P & ExternalProps<C, A>;
onActionClicked 的错误已删除!这对我来说似乎很奇怪,因为 HocProps 的类型定义与被包装组件的 prop 类型无关!然而,这种“解决方案”对我来说是不可取的,因为现在InjectedProps<A> 也可以由HOC 的用户注入。
问题
那么我哪里错了?
我是否正确假设 ref 回调不起作用,因为
Wrapped Component类型是React.ComponentType<P>而不是React.ComponentClass<P>?为什么将
Wrapped Component类型更改为React.ComponentClass<P>会导致Wrapped Component的onActionClicked属性出现编译错误?为什么更改
HocProps的类型别名会消除onActionClicked属性上的错误?他们不是完全没有关系吗?我编写的
Subtract功能是否正确?这是错误的来源吗?
任何帮助将不胜感激,所以提前致谢!
【问题讨论】:
-
This bug 是问题的一部分。有机会我会多写的。
-
感谢您的努力,Matt McCutchen!我很想尽快收到您的来信。
标签: javascript reactjs typescript react-native higher-order-components