使用 TypeScript 装饰器和 promises 并正确输入?

Using TypeScript decorators and promises with proper typing?

我写了一个 buffered 装饰器,它通过返回 Promise(在执行期间)按预期工作。但是,为了让 tsc 转译器满意,我不得不用 any 转换装饰函数,然后(在下面的示例中)用 Promise<number>.

如何避免额外的转换?装饰器甚至可以修改装饰函数的签名吗?例如,我如何编写一个 @buffered 装饰器,它采用方法函数 f (其中 returns number),而装饰函数 @buffered f returns一个Promise<number>不是只是一个number)?:

import { decorator as buffered } from '@dizmo/functions-buffered';

class Class {
    @buffered(100) // i.e. 100ms delay
    public f1(t: Date): number {
        return new Date().getTime() - t.getTime();
    }
    @buffered // i.e. defaults to 200ms delay
    public f2(t: Date): number {
        return new Date().getTime() - t.getTime();
    }
}

const p: Promise<number>
    = new Class().f1(new Date()) as any;

p.then((res: number) => { console.debug(res); });

我可以这样写:

const p: Promise<number> = new Class().f1(new Date());
p.then((res: number) => { console.debug(res); });

但是,我得到以下输出:

[ts] Type 'number' is not assignable to type 'Promise<number>'.

然而,我实际上应该只能写:

const p = new Class().f1(new Date());
p.then((res: number) => { console.debug(res); });

但是 p 被识别为 number 而不是 Promise:

[ts] Property 'then' does not exist on type 'number'.

@buffered 装饰器的实现位于:

请注意,当我想使用 200ms.

的默认延迟时,由于我希望删除 @buffered 装饰器的括号,因此实现具有额外的复杂性

装饰者不能改变他们正在装饰的 class 的结构。这是一个设计限制。有一项针对 change this 的提议,但它似乎不是优先事项(如果它对你很重要,也许可以投票支持 GitHub 问题)

如果我们想要转换所有方法,我们可以使用映射类型,但我们不能将映射类型转换仅应用于修饰的属性,因此在这种情况下这不是一个可行的解决方案。

更好的选择是在 class 中直接输入正确的 return。我们可以使用 async:

对代码进行最少的更改来做到这一点
class Class {
    @buffered(100) // i.e. 100ms delay
    // use async and return a promise instead of a number
    public async f1(t: Date): Promise<number> {
        return new Date().getTime() - t.getTime();
    }
    @buffered // i.e. defaults to 200ms delay
    public async f2(t: Date) { // no need to specify the return type will be inferred correctly to Promise<number>
        return new Date().getTime() - t.getTime();
    }
}

const p: Promise<number> = new Class().f1(new Date()); // works fine

p.then((res: number) => { console.debug(res); });