Обходной путь для доступа к аргументам типа класса в статическом методе в Typescript - PullRequest
0 голосов
/ 26 сентября 2018

Следующая ошибка

Статические члены не могут ссылаться на параметры типа класса.

получается из следующего фрагмента кода

abstract class Resource<T> {
    /* static methods */
    public static list: T[] = [];
    public async static fetch(): Promise<T[]> {
        this.list = await service.get();
        return this.list;
    }
    /*  instance methods */ 
    public save(): Promise<T> {
        return service.post(this);
    }
}

class Model extends Resource<Model> {
}

/* this is what I would like, but the because is not allowed because :
"Static members cannot reference class type parameters."
*/

const modelList = await Model.fetch() // inferred type would be Model[]
const availableInstances = Model.list // inferred type would be Model[]
const savedInstance = modelInstance.save() // inferred type would be Model

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

interface Instantiable<T> {
    new (...args: any[]): T;
}
interface ResourceType<T> extends Instantiable<T> {
    list<U extends Resource>(this: ResourceType<U>): U[];
    fetch<U extends Resource>(this: ResourceType<U>): Promise<U[]>;
}

const instanceLists: any = {} // some object that stores list with constructor.name as key

abstract class Resource {
    /* static methods */
    public static list<T extends Resource>(this: ResourceType<T>): T[] {
        const constructorName = this.name;
        return instanceLists[constructorName] // abusing any here, but it works :(
    }
    public async static fetch<T extends Resource>(this: ResourceType<T>): Promise<T[]> {
        const result = await service.get()
        store(result, instanceLists) // some fn that puts it in instanceLists
        return result;
    }
    /*  instance methods */ 
    public save(): Promise<this> {
        return service.post(this);
    }
}
class Model extends Resource {
}
/* now inferred types are correct */
const modelList = await Model.fetch() 
const availableInstances = Model.list 
const savedInstance = modelInstance.save()

Проблема, с которой я столкнулся, заключается в том, что переопределение статических методов становится действительно утомительным.Выполнение следующих действий:

class Model extends Resource {

    public async static fetch(): Promise<Model[]> {
        return super.fetch();
    } 
}

приведет к ошибке, поскольку Model больше не правильно расширяет Resource из-за другой подписи.Я не могу придумать способ объявить метод выборки без ошибок, не говоря уже о простом и интуитивно понятном способе перегрузки.

Единственный способ обойти меня - это следующее:

class Model extends Resource {
    public async static get(): Promise<Model[]> {
        return super.fetch({ url: 'custom-url?query=params' }) as Promise<Model[]>;
    }
}

На мой взгляд, это не очень приятно.

Есть ли способ переопределить метод выборки без необходимости вручную приводить к Model и выполнять трюки с обобщениями?

1 Ответ

0 голосов
/ 26 сентября 2018

Вы можете сделать что-то вроде этого:

function Resource<T>() {
  abstract class Resource {
    /* static methods */
    public static list: T[] = [];
    public static async fetch(): Promise<T[]> {
      return null!;
    }
    /*  instance methods */
    public save(): Promise<T> {
      return null!
    }
  }
  return Resource;
}

В приведенном выше примере Resource является обобщенной функцией, которая возвращает локально объявленный класс .Возвращенный класс не универсальный, поэтому его статические свойства и методы имеют конкретные типы для T.Вы можете расширить его следующим образом:

class Model extends Resource<Model>() {
  // overloading should also work
  public static async fetch(): Promise<Model[]> {
    return super.fetch();
  }
}

Все имеет типы, которые вы ожидаете:

 Model.list; // Model[]
 Model.fetch(); // Promise<Model[]>
 new Model().save(); // Promise<Model>

Так что это может сработать для вас.

Единственные предостережения, которые я вижу сейчас:

  • В class X extends Resource<X>() есть небольшое дублирование, которое не идеально, но я не думаю, что вы можетеполучите контекстную типизацию, позволяющую выводить вторую X.

  • Локально объявленные типы, как правило, не экспортируются и не используются в качестве объявлений, поэтому вам может потребоваться быть осторожным или приходитьобойти обходные пути (например, экспортировать какой-либо структурно идентичный или структурно достаточно близкий тип и объявить, что Resource - это тот тип?).

В любом случае надеюсь, что это поможет.Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...