Класс-метод в Typescript - PullRequest
       19

Класс-метод в Typescript

0 голосов
/ 19 октября 2018

Я хочу иметь возможность использовать метод на основе классов в Typescript - то есть метод, определенный в базовом классе, который при вызове в подклассе может получить доступ к подклассу, из которого он был вызван.Такое поведение возможно в Python, но, насколько я могу судить, в Typescript нет эквивалента.Как лучше всего повторить это в Typescript?

Пример (на Python):

Этот пример (написанный на Python) демонстрирует то, что я могу захотеть сделатьс этим.

# Base class
class Model:
    objects = []

    def __init__(self, objId):
        self.objId = objId

        Model.objects.append(self)

    @classmethod
    def getById(cls, objId):
        # Method can access subclass using "cls" parameter
        objects = [obj for obj in cls.objects if obj.objId == objId]
        if len(objects) > 0:
            return objects[0]
        else:
            print("error")

# Subclass
class Foo(Model):
    def __init__(self, objId, name):
        self.objId = objId
        self.name = name

        Foo.objects.append(self)

Foo(1, "foo")
Foo(3, "bar")

# Call method on subclass
foo = Foo.getById(1)
bar = Foo.getById(3)

print(foo.name)  # outputs "foo"
print(bar.name)  # outputs "bar"

Foo.getById(2)  # outputs "error"

Машинопись (не работает):

Этот пример показывает грубый эквивалент в машинописи, но он не работает из-за отсутствияметоды класса.

class Model {
    static objects: Model[]

    id: number

    constructor (id) {
        this.id = id

        Model.objects.push(this);
    }

    // Here "cls" should refer to the class on which this method is called
    static getById (id): cls {
        let item = cls.objects.find(obj => obj.id == id);
        if (item === undefined) {
            console.log("error");
        } else {
            return item;
        }
    }
}

class Foo extends Model {
    name: string

    constructor (id, name) {
        super(id);

        this.name = name

        Foo.objects.push(this);
    }
}


new Foo(1, "foo");
new Foo(3, "bar");

// Here cls === Foo
let foo = Foo.getById(1);
let bar = Foo.getById(3);

console.log(foo.name);
console.log(bar.name);

Foo.getById(2)

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

Дополнительный вопрос:

Есть ли способ иметь статическое свойство "objects" для каждого подкласса, каждый из которых напечатан в своем подклассе, без необходимости повторного ввода вручную?-declaring его.

class Model {
    static objects: Model[]

class Foo extends Model {
    static objects: Foo[]

class Bar extends Model {
    static objects: Bar[]

По сути, я хочу это поведение, но без необходимости отдельно объявлять свойство "objects" для каждого подкласса.Есть ли способ сделать это?

1 Ответ

0 голосов
/ 19 октября 2018

Контекст this статического метода - это класс, к которому он был вызван.Вы можете переопределить тип this для getById, чтобы указать, что getById можно вызывать для любого подкласса Model (т. Е. Для любого объекта с сигнатурой конструкции, которая создает подтип Model), и возвращает экземпляртип этого класса.Вот пример кода, предполагая, что все объекты всех подклассов хранятся в одном массиве Model.objects:

class Model {
    static objects: Model[]

    id: number

    constructor (id) {
        this.id = id

        Model.objects.push(this);
    }

    static getById<M extends Model>(
        this: { new(...args: any[]): M }, id): M {
        let item = Model.objects.find(obj => obj.id == id);
        if (item === undefined) {
            console.log("error");
        } else {
            return <M>item;
        }
    }
}

Обратите внимание, что это нецелесообразно, потому что кто-то может вызвать Foo.getById с идентификатором, который соответствуетBar вместо Foo.

Если вы хотите отдельный массив objects для каждого подкласса, вам нужно будет вручную инициализировать массив в каждом подклассе (автоматизировать это невозможно)измените конструктор Model так, чтобы он помещался в массив objects текущего класса (на который вы можете ссылаться как this.constructor после его объявления), а затем измените getById на использование this.objects, объявив, что он долженсуществует.

class Model {
    static objects: Model[]
    "constructor": {
        // This implementation is slightly unsound: the element type
        // is actually the instance type of the constructor.  At least
        // the interface provided is safe.
        objects: Model[]
    };

    id: number

    constructor (id) {
        this.id = id

        this.constructor.objects.push(this);
    }

    static getById<M extends Model>(
        this: { new(...args: any[]): M, objects: M[] }, id): M {
        let item = this.objects.find(obj => obj.id == id);
        if (item === undefined) {
            console.log("error");
        } else {
            return item;
        }
    }
}
...