1

我有一个看起来像这样的基本模型类:

class Base {
    abstract public save(): Promise<Base>;
}

我的模型继承自 Base 并且必须实现该save方法。例如,

class User extends Base {
    public save(): Promise<User> {
        // save my user in the database and return a promise like this...
        return getDatabaseConnection()
            .then((db) => db.insert(...))
            .then((res) => this);
    }
}

这工作正常,Typescript 对这里的类型没有问题。

但随着我的发展,我注意到我的模型save方法都非常相似......

class Group extends Base {
    public save(): Promise<Group> {
        // save my group in the database and return a promise like this...
        return getDatabaseConnection()
            .then((db) => db.insert(...))
            .then((res) => this);
    }
}

class OtherModel extends Base {
    public save(): Promise<OtherModel> {
        // save my other model in the database and return a promise like this...
        return getDatabaseConnection()
            .then((db) => db.insert(...))
            .then((res) => this);
    }
}

所以我突然想到,为了保持干燥,我可以实现一个装饰器来将save方法添加到类中。

const save = (table: string, idColumn: string) => {
    return function decorator<T extends {new(...args: any[]): {}}>(target: T): T {
        return class extends target {
            public save = function() {
                // save whatever model this is & return it
                return getDatabaseConnection()
                    .then((db) => db.insert(...use the table, idColumn args...))
                    .then((res) => this);
            };
        }
    };
};

@save('user_table', 'id')
class User extends Base {}

@save('group_table', 'id')
class Group extends Base {}

@save('other_table', 'id')
class OtherModel extends Base {}

它就像一个魅力,除了......打字稿抱怨save我的类声明中缺少抽象方法。

我一直在用@ts-ignore语句解决它,但我想删除它们。

我遇到了这个问题,它是关于通过装饰器添加到类的方法,我知道装饰器不是为了修改接口或契约,但这不是我在这里做的。我正在尝试实现每个接口存在(并且必须实现)的抽象方法。

当我通过装饰器执行抽象方法时,如何让 Typescript 识别出我确实实现了抽象方法?

4

1 回答 1

3

我认为您所问问题的答案是您应该对所有子类的方法使用明确的赋值断言save(),这告诉编译器该方法已实现,即使它无法验证这一点。像这样:

@save('user_table', 'id')
class User extends Base {
    save!: () => Promise<this>;
}

@save('group_table', 'id')
class Group extends Base {
    save!: () => Promise<this>;
}

@save('other_table', 'id')
class OtherModel extends Base {
    save!: () => Promise<this>;
}

这将抑制错误,尽管它有点重复并且需要比我预期的更多的手动注释。


这里的另一种方法可能是使用类工厂而不是装饰器。您可以使类扩展任何计算结果为类构造函数的表达式。这与您使用装饰器所做的并没有太大不同。首先我们制作工厂:

const Save = (table: string, idColumn: string) => class extends Base {
    public save() {
        // save whatever model this is & return it
        return Promise.resolve(this);
    }
};

上述方法的实现save()可以是任何你想要的,只要它适合Base. 与装饰器的区别在于,调用Save直接返回一个扩展类Base,而不是返回一个扩展传递给它的类构造函数的函数。然后你的子类变成:

class User extends Save('user_table', 'id') {

}

class Group extends Save('group_table', 'id') {

}

class OtherModel extends Save('other_table', 'id') {

}

这些被自动视为正确的子类,Base因此无需担心任何错误。


好的,希望有帮助;祝你好运!

链接到代码

于 2019-12-05T01:24:47.313 回答