我正在尝试在TypeScript(2.8)中使用React文档(React 16.3)中的HOC来实现示例使用上下文,并且失败了。作为参考,React手册中的代码:
const ThemeContext = React.createContext('light'); // This function takes a component... export function withTheme(Component) { // ...and returns another component... return function ThemedComponent(props) { // ... and renders the wrapped component with the context theme! // Notice that we pass through any additional props as well return ( <ThemeContext.Consumer> {theme => <Component {...props} theme={theme} />} </ThemeContext.Consumer> ); }; }
我能想到的最好的方法是:
export interface ThemeAwareProps { theme: string; } const ThemeContext = React.createContext('light'); export function withTheme<P extends ThemeAwareProps, S>(Component: new() => React.Component<P, S>) { return function ThemedComponent(props: P) { return ( <ThemeContext.Consumer> {theme => <Component {...props} theme={theme} />} </ThemeContext.Consumer> ); }; } class App extends React.Component { public render() { return ( <ThemeContext.Provider value={'dark'}> <ThemedButton/> </ThemeContext.Provider> ); } }
ThemedButton.tsx:
interface ThemedButtonProps extends ThemeAwareProps { } interface ThemedButtonState{ } class ThemedButton extends React.Component<ThemedButtonProps, ThemedButtonState> { constructor(props: ThemedButtonProps) { super(props); } public render() { return ( <button className={this.props.theme}/> ) } } export default withTheme(ThemedButton);
问题是最后一行(export default withTheme(ThemedButton))。TypeScript编译器抱怨
export default withTheme(ThemedButton)
类型的typeof ThemedButton参数不能分配给类型的参数new () => Component<ThemedButtonProps, ThemedButtonState, any>。
typeof ThemedButton
new () => Component<ThemedButtonProps, ThemedButtonState, any>
我想念什么?
您大部分情况都是正确的,但缺少一些内容:
对于Component,请使用React.ComponentType<Props>,它正确接受类组件和功能组件。我认为new () => ...单独使用在这里不起作用,因为签名不能完全匹配。
Component
React.ComponentType<Props>
new () => ...
要ThemedButton在使用道具时将其排除在外,您必须使用一些看起来很神奇的语法:
ThemedButton
function ThemedComponent(props: Pick
>)
这是这样做的:
Exclude<keyof P, keyof ThemeAwareProps>
P
ThemeAwareProps
Pick<P, ...>
将这些结合起来,便得到了一个组件,它接受所有可以做的道具ThemedButton,减去theme道具,这样我们就可以做到<ThemedButton />没有错误。
theme
<ThemedButton />
这是完整的HOC:
function withTheme<P extends ThemeAwareProps>(Component: React.ComponentType<P>) { return function ThemedComponent(props: Pick<P, Exclude<keyof P, keyof ThemeAwareProps>>) { return ( <ThemeContext.Consumer> {(theme) => <Component {...props} theme={theme} />} </ThemeContext.Consumer> ) } }
最后,关于该主题的一篇不错的博客文章,我从中收集了大部分此类信息。如果您愿意,它还包括一种缩短类型的Pick<...>内容的方法Omit。
Pick<...>
Omit
编辑:休息/传播的行为已在3.2中更改,并且此错误作为不幸的副作用出现,导致props与其他道具合并时被擦除的类型。当前可行的解决方法是强制转换props为P:
props
return ( <ThemeContext.Consumer> {(theme) => <Component {...props as P} theme={theme} />} </ThemeContext.Consumer> )