代码之家  ›  专栏  ›  技术社区  ›  curious-cat

Typescript高阶组件作为装饰器

  •  4
  • curious-cat  · 技术社区  · 8 年前

    我试图在我的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 回复  |  直到 8 年前
        1
  •  4
  •   Adrian Leonhard    8 年前

    返回值的类装饰器类似于

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

    在您的示例中,呈现类型签名不匹配,但使用添加的方法,问题更加明显:使用装饰器的实现,以下操作将失败:

    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