打字稿高级订单组件作为装饰器

时间:2017-10-25 19:06:37

标签: reactjs typescript higher-order-components

我正在尝试在我的React项目中使用Typescript,但我却陷入了让我的HOC运行的类型。这是一个展示我遇到问题的最小例子:

const withDecorator =
    (Wrapped: React.ComponentType): React.ComponentClass =>
        class withDecorator extends Component {
            render() {
                return <Wrapped {...this.props} />
            }
        }

@withDecorator
class Link extends Component<object, object> {
    render() { return <a href="/">Link</a> }
}

这将返回错误:

'Unable to resolve signature of class decorator when called as an expression.
Type 'ComponentClass<{}>' is not assignable to type 'typeof Link'.
    Type 'Component<{}, ComponentState>' is not assignable to type 'Link'.
    Types of property 'render' are incompatible.
        Type '() => string | number | false | Element | Element[] | ReactPortal | null' is not assignable to type '() => Element'.
        Type 'string | number | false | Element | Element[] | ReactPortal | null' is not assignable to type 'Element'.
            Type 'null' is not assignable to type 'Element'.'

我真的不明白为什么会出现这种错误。我一定做错了什么。一旦我介绍道具,事情会变得更加毛茸茸。

我非常感谢正确的解决方案,但我也非常有兴趣了解为什么会出现这种错误。

谢谢!

1 个答案:

答案 0 :(得分:4)

返回值的类装饰器类似于执行

const Link = withDecorator(class extends Component<object, object> {
    render() { 
        return <a href="/">Link</a> 
    }
    instanceMethod() { return 2 }
    static classMethod() { return 2 }
})

TypeScript期望装饰器的返回值与输入具有相同的类型,因此结果仍然具有相同的行为。在您的示例中,渲染类型签名不匹配,但是添加的方法问题更明显:使用装饰器的实现,以下将失败:

new Link().instanceMethod()
Link.classMethod()

正确的类型签名是:

function withDecorator<T extends React.ComponentClass>(Wrapped: T): T

并且实现也应该匹配,最容易通过扩展目标类:

return class extends Wrapped { ... }

请注意,使用React HOC,您不一定要扩展类,因此使用装饰器可能不是最佳解决方案。

另见https://github.com/Microsoft/TypeScript/issues/9453