在打字稿中抽象通用Container类的子项的装饰器

时间:2018-06-18 09:39:12

标签: reactjs typescript generics typescript-decorator typescript2.6

我遇到了Typescript泛型问题。 我正在使用Typescript 2.6

基本思想是我想创建一个暴露类装饰器的MobX存储库,添加基本的身份验证检查,而装饰器接受从抽象通用Container类派生的类类型(React Router路由容器,例如)。

以下是导致此问题的代码:

interface Newable<T, U = any> {
  new (...args: U[]): T;
}

function withAuth<TContainer extends Container<TProps, TState>, TProps extends Container.Props, TState>(ctor: Newable<TContainer>): Newable<TContainer> {
  return class WithAuth extends ctor {
    public async componentWillMount() {
      await this.props.stores.authentication.tryLoggingIn();
      super.componentWillMount();
    }

    public async componentWillUpdate() {
      await this.props.stores.authentication.tryLoggingIn();
      super.componentWillUpdate();
    }

    public render(): React.ReactNode {
      return super.render();
    }
  };
}

派生类可以重新定义TPropsTState泛型参数,因为它们可能有更多的React道具或复杂状态。

我从Typescript编译器获得的错误消息:

Type 'typeof WithAuth' is not assignable to type 'Newable<TContainer, any>'. Type 'WithAuth' is not assignable to type 'TContainer'. (line 6)
Base constructor return type 'TContainer' is not a class or interface type. (line 6)
Property 'props' does not exist on type 'WithAuth'. (line 8)
Property 'props' does not exist on type 'WithAuth'. (line 14)

删除withAuth函数的返回类型会导致此错误:

Base constructor return type 'TContainer' is not a class or interface type.

以下是Container类的相关代码(简化,它包含更多内容):

abstract class Container<T extends Container.Props = Container.Props, U extends React.ComponentState = {}> extends React.Component<T, U> {
  public abstract render(): React.ReactNode;
}

namespace Container {
  export type Props = React.Props<any>; // TODO: typings
}

我没有得到的是,即使使用非泛型类而不是Container导致

Base constructor return type 'TContainer' is not a class or interface type.

错误。但是,正如您在泛型参数声明中所看到的,TContainer IS DERIVED来自一个类。从我得到的,这应该不是一个问题。

所以我的问题是,如何为Container<TProps, TState>创建一个类装饰器,将componentWillMountcomponentWillUpdate添加身份验证检查?

非常感谢!

1 个答案:

答案 0 :(得分:2)

您正在尝试使用mixins,其描述为here。它的工作方式非常特殊,要扩充的类必须作为类型参数传入,可以约束它来扩展抽象类。请注意,由于我们要求类具有可调用的构造函数,因此传递给withAuth的类不能是Container,而是从Container派生的类并实现抽象方法已经。

abstract class Container<T extends Container.Props = Container.Props, U extends React.ComponentState = {}> extends React.Component<T, U> {
    public abstract render(): React.ReactNode;
}

namespace Container {
    export type Props = {
        stores: any
    };
}

interface Newable<T, U = any> {
    new(...args: U[]): T;
}

function withAuth<TCtor extends Newable<Container<Container.Props, any>>>(ctor: TCtor) {
    return class WithAuth extends ctor {
        public async componentWillMount() {
            await this.props.stores.authentication.tryLoggingIn();
            this.componentWillMount();
        }

        public async componentWillUpdate() {
            await this.props.stores.authentication.tryLoggingIn();
            this.componentWillUpdate();
        }

        public render(): React.ReactNode {
            return this.render();
        }
    };
}

class NewComponent extends Container< Container.Props & { otherProp: string}, any> {
    public render(): React.ReactNode {
        // Actual implementation 
        throw new Error("Method not implemented.");
    }

}
const NewComponentWitAuth = withAuth(NewComponent);

let witAuth = <NewComponentWitAuth otherProp="" stores={null}  /> // props type is preserved