Заставить подкласс использовать свой собственный тип в суперклассе в TypeScript

#typescript #generics #inheritance #typescript-generics

Вопрос:

Рассмотрим следующие типы :

 abstract class IProject { /* Shared stuff */ }
type ActionList<TProject extends IProject> = Array<{ (project: TProject): void }>;
 

Представьте, что это библиотека, которая будет опубликована; и пользователи должны расширить IProject класс, как показано ниже :

 class ProjectA extends IProject {
    public constructor(public readonly actions: ActionList<ProjectA>) { }
    /* ProjectA stuff */
}
class ProjectB extends IProject {
    public constructor(public readonly actions: ActionList<ProjectB>) { }
    /* ProjectB stuff */
}
 

Предполагается, что расширенные классы должны иметь actions поля и иметь свой собственный тип, а не суперкласс.
Первая цель состоит в том, чтобы заставить пользователей включать это поле и без избыточности повторять это поле в каждом подклассе.
Просто для ясности, для суперкласса нормально не знать тип дочернего элемента; только в конструкторах подклассов требуется использовать тип подкласса в их ActionList s.
Я могу сделать следующее, чтобы достичь первой цели :

 type ActionList<TProject> = Array<{ (project: TProject): void }>;
abstract class IProject<TChild> {
    protected constructor(public readonly actions: ActionList<TChild>) { }
    /* Shared stuff */
}
 

Теперь пользователи должны расширить свои классы, как показано ниже, что является грязным :

 class ProjectA extends IProject<ProjectA> {
    public constructor(actions: ActionList<ProjectA>) { super(action); }
    /* ProjectA stuff */
}
class ProjectB extends IProject<ProjectB> {
    public constructor(actions: ActionList<ProjectB>) { super(action); }
    /* ProjectB stuff */
}
 

Но это на самом деле не заставляет пользователей использовать тип своего собственного класса в его ActionList параметрах типа, потому что в этом случае TChild может быть любой тип; в то время как я хочу, чтобы они были подклассами IProject ; чтобы быть более конкретным, точный подкласс, который расширяется IProject .
Я могу отделить базовый интерфейс; как показано ниже :

 interface IProjectBase {
    /* Non-child related shared stuff */
}
type ActionList<TProject extends IProjectBase> = Array<{ (project: TProject): void }>;
abstract class IProject<TChild extends IProjectBase> implements IProjectBase {
    protected constructor(public readonly actions: ActionList<TChild>) { }
    /* Child related shared stuff */
}
 

Теперь TChild он ограничен в расширении IProjectBase , но теперь пользователи могут использовать его таким образом :

 class ProjectA extends IProject<ProjectA> {
    public constructor(actions: ActionList<ProjectA>) { super(action); }
    /* ProjectA stuff */
}
class ProjectB extends IProject<ProjectA> {
    public constructor(actions: ActionList<ProjectA>) { super(action); }
    /* ProjectB stuff */
}
 

В то время ProjectB как не следует разрешать использовать ProjectA в качестве параметра типа; только себя.
Итак, каков обходной путь для этого ? Есть ли способ ограничить проекты, реализованные пользователями, включением в параметры типов только их собственных ActionList типов ?
Обходной путь не требует использования универсальных методов, единственные цели здесь-не потерять информацию о типе и не заставить пользователей включать этот конкретный тип actions поля в свои классы.

Комментарии:

1. Я не верю, что это возможно.