У меня проблемы с дженериками 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();
}
};
}
И общие параметры TProps, и TState могут быть переопределены производным классом, поскольку они могут иметь больше свойств 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
}
Чего я не понимаю, так это то, что даже использование неуниверсального класса вместо контейнера приводит к
Base constructor return type 'TContainer' is not a class or interface type.
ошибка. Однако, как вы можете видеть в объявлении универсального параметра, TContainer ПРОИЗВОДИТСЯ из класса. Насколько я понимаю, это не должно быть проблемой.
Итак, мой вопрос: как я могу создать декоратор класса для Container<TProps, TState>, который добавлял бы проверки аутентификации для componentWillMount и componentWillUpdate?
Огромное спасибо!





Вы пытаетесь использовать миксины, которые описаны здесь. То, как это работает, довольно специфично, класс, который будет расширен, должен быть передан как параметр типа, который может быть ограничен для расширения абстрактного класса. Обратите внимание: поскольку мы передаем классу вызываемый конструктор, класс, передаваемый в 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
Ну ладно, в этом есть смысл! Большое спасибо за ссылку!