【问题标题】:How can I get Typescript to recognize that I actually did implement abstract methods when I do it via a decorator?当我通过装饰器执行抽象方法时,如何让 Typescript 识别出我确实实现了抽象方法?
【发布时间】:2020-03-29 20:47:29
【问题描述】:

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

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);
    }
}

所以我突然想到,为了保持 DRY,我可以实现一个装饰器来将 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 {}

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

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

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

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

【问题讨论】:

    标签: typescript decorator typescript-decorator


    【解决方案1】:

    我认为你的问题的答案是你应该在你的所有子类上为save() 方法使用definite assignment assertion,它告诉编译器该方法已经实现,即使它无法验证这一点.像这样:

    @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>;
    }
    

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


    这里的另一种方法可能是使用类工厂而不是装饰器。你可以上课extend any expression that evaluates to a class constructor。这与您使用装饰器所做的并没有太大不同。首先我们制作工厂:

    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 的正确子类,因此无需担心任何错误。


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

    Link to code

    【讨论】:

    • 谢谢!我认为类工厂方法是可行的方法,特别是考虑到save 不是我“装饰”的唯一方法(第一种方法需要在我的类上使用很多非功能性符号)。跨度>
    猜你喜欢
    • 2019-11-12
    • 2012-01-08
    • 1970-01-01
    • 2013-05-30
    • 2016-08-13
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    • 1970-01-01
    相关资源
    最近更新 更多