为注入 prop 的 HOC 定义正确的类型
Defining the correct types for an HOC injecting a prop
所以我有一组基础组件,其属性类型设置为:
type SomeProps = {
// ... arbitrary prop types
theme: {
wrapper: string,
button: string,
// these are also kind of arbitrary
}
}
现在我有一个 HOC,它将注入 theme
属性,这样这个新创建的组件的用户就不必 t/shouldn 传递 theme
属性了。测试表明下面的代码不起作用(合并同一字段的类型)...
type A = { foo: number };
type B = A & { foo?: number };
const x: B = { foo: 2 };
无论如何,我编写了 HOC,但我不确定流类型是否适用于我描述的场景。作为附加要求,新创建的组件仍然可以传递一个 theme
属性,它应该与注入的 theme
合并。这是 HOC 的代码:
// @flow
import withProps from 'recompose/withProps';
import setDisplayName from 'recompose/setDisplayName';
import wrapDisplayName from 'recompose/wrapDisplayName';
import classnames from 'classnames';
type FunctionComponent<P> = (props: P) => ?React$Element<any>;
type ClassComponent<D, P, S> = Class<React$Component<D, P, S>>;
type Component<P> = FunctionComponent<P> | ClassComponent<any, P, any>;
type ThemeType = { [className: string]: string };
type OptionalThemePropType = {
[prop: string]: mixed,
theme?: ThemeType
}
function mergeTheme<T, U: T & { theme: ThemeType }>(
injectedTheme: ThemeType
): (BaseComponent: Component<T>) => Component<U> {
return BaseComponent => {
const Themed: Component<U> = withProps((ownProps: OptionalThemePropType) => {
let theme: ThemeType = injectedTheme;
if (ownProps && ownProps.theme) {
const ownTheme: ThemeType = ownProps.theme;
theme = Object
.keys(ownTheme)
.filter((key: string) => !!injectedTheme[key])
.reduce(
(accum: ThemeType, key: string) => {
accum[key] = classnames(ownTheme[key], injectedTheme[key]);
return accum;
},
{ ...ownTheme, ...injectedTheme }
);
}
return { theme };
})(BaseComponent);
setDisplayName(wrapDisplayName(BaseComponent, 'themed'))(Themed);
return Themed;
};
}
这是正确的吗?
想法是使用 $Diff
flowtype helper 从 Component Props 中删除注入的 props。
@expo/ex-navigation 包的 withNavigation 示例
declare type FunctionComponent<P> = (props: P) => ?React$Element<any>;
declare type ClassComponent<D, P, S> = Class<React$Component<D, P, S>>;
declare type Component<P> = FunctionComponent<P> | ClassComponent<any, P, any>;
declare type ExNavProps = {
navigation: NavigationContext,
navigator: ExNavigatorContext,
};
declare type WithNavigationOptions = {
withRef: boolean,
};
// withNavigation should add navigator and navigation as defaultprops for WrappedComponent
declare function withNavigation<DefaultProps, Props, State>(
WrappedComponent: ClassComponent<DefaultProps, Props, State>,
options?: WithNavigationOptions,
): ClassComponent<DefaultProps & ExNavProps, $Diff<Props, ExNavProps>, State>;
但是您仍然需要在组件中定义 ExNavProps,就像这样
类型道具 = {
firstProp:字符串,
} & ExNavProps
export default class MyComponent extends React<void, Props, void>
所以我有一组基础组件,其属性类型设置为:
type SomeProps = {
// ... arbitrary prop types
theme: {
wrapper: string,
button: string,
// these are also kind of arbitrary
}
}
现在我有一个 HOC,它将注入 theme
属性,这样这个新创建的组件的用户就不必 t/shouldn 传递 theme
属性了。测试表明下面的代码不起作用(合并同一字段的类型)...
type A = { foo: number };
type B = A & { foo?: number };
const x: B = { foo: 2 };
无论如何,我编写了 HOC,但我不确定流类型是否适用于我描述的场景。作为附加要求,新创建的组件仍然可以传递一个 theme
属性,它应该与注入的 theme
合并。这是 HOC 的代码:
// @flow
import withProps from 'recompose/withProps';
import setDisplayName from 'recompose/setDisplayName';
import wrapDisplayName from 'recompose/wrapDisplayName';
import classnames from 'classnames';
type FunctionComponent<P> = (props: P) => ?React$Element<any>;
type ClassComponent<D, P, S> = Class<React$Component<D, P, S>>;
type Component<P> = FunctionComponent<P> | ClassComponent<any, P, any>;
type ThemeType = { [className: string]: string };
type OptionalThemePropType = {
[prop: string]: mixed,
theme?: ThemeType
}
function mergeTheme<T, U: T & { theme: ThemeType }>(
injectedTheme: ThemeType
): (BaseComponent: Component<T>) => Component<U> {
return BaseComponent => {
const Themed: Component<U> = withProps((ownProps: OptionalThemePropType) => {
let theme: ThemeType = injectedTheme;
if (ownProps && ownProps.theme) {
const ownTheme: ThemeType = ownProps.theme;
theme = Object
.keys(ownTheme)
.filter((key: string) => !!injectedTheme[key])
.reduce(
(accum: ThemeType, key: string) => {
accum[key] = classnames(ownTheme[key], injectedTheme[key]);
return accum;
},
{ ...ownTheme, ...injectedTheme }
);
}
return { theme };
})(BaseComponent);
setDisplayName(wrapDisplayName(BaseComponent, 'themed'))(Themed);
return Themed;
};
}
这是正确的吗?
想法是使用 $Diff
flowtype helper 从 Component Props 中删除注入的 props。
@expo/ex-navigation 包的 withNavigation 示例
declare type FunctionComponent<P> = (props: P) => ?React$Element<any>;
declare type ClassComponent<D, P, S> = Class<React$Component<D, P, S>>;
declare type Component<P> = FunctionComponent<P> | ClassComponent<any, P, any>;
declare type ExNavProps = {
navigation: NavigationContext,
navigator: ExNavigatorContext,
};
declare type WithNavigationOptions = {
withRef: boolean,
};
// withNavigation should add navigator and navigation as defaultprops for WrappedComponent
declare function withNavigation<DefaultProps, Props, State>(
WrappedComponent: ClassComponent<DefaultProps, Props, State>,
options?: WithNavigationOptions,
): ClassComponent<DefaultProps & ExNavProps, $Diff<Props, ExNavProps>, State>;
但是您仍然需要在组件中定义 ExNavProps,就像这样
类型道具 = { firstProp:字符串, } & ExNavProps
export default class MyComponent extends React<void, Props, void>