微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Typescript方法装饰器行为异常

如何解决Typescript方法装饰器行为异常

我有这个代码示例,它说明了我的代码中发生的一件奇怪的事情,但我不知道发生这种情况的原因。

基本上,我有一个简单的类Foo,它仅包含一个方法。一种问候方法认情况下在调用时会简单地记录为“ Hello,World”。但是我将装饰器应用于更改方法方法。新功能几乎相同,但是使用字符串参数来确定要记录的内容

这是我的示例的完整代码

const arr = [];

function methodoverride(target,propertyKey,descriptor) {
    descriptor.value = function(str) {
        console.log("Hello,",str);
    };
    return descriptor;
}


class Foo {
    
    @methodoverride
    greet() {
        console.log("Hello,World");
    }
}

let foo = new Foo();
arr.push(foo);

// foo.greet(); Will result in "Hello,undefined"
// foo.greet("FooBar"); Will result in error.
arr[0].greet("FooBar");

在我的示例中,您可以看到我创建了一个Foo实例foo,并将其推入数组。我会回到为什么要这么做。但是,如果我尝试运行foo.greet(),它将在执行已编译的js时记录Hello,undefined。显然是在争执,因为方法已被覆盖。
但是,如果我将参数foo.greet("FooBar");传递给它,Typescript将抛出错误error TS2554: Expected 0 arguments,but got 1.

更奇怪的是,如果我尝试从插入到数组中的对象执行该方法,它将很好地工作。示例中的最后一行代码arr[0].greet("FooBar");将记录Hello,FooBar

此外,如果我从数组中取出该对象并将其存储在另一个变量中,将其称为bar,则它传递参数将不会出错。

在我的代码示例下添加内容将获得注释中列出的结果。

let bar = arr[0];
bar.greet(); // Hello,undefined
bar.greet("FooBar"); // Hello,FooBar

我希望这些例子足够清楚,任何人都可以仿效。我是TypeScript装饰器的新手,我不确定是否有装饰器我没有得到的东西,或者问题出在transpiler内部。

我正在使用全局安装的https://github.com/damonleelcx/PureSign将TS转换为JS。我使用的版本是3.9.7。

这是我用来翻译的命令:tsc -p tsconfig.json

这是我的tsconfig.json文件

{      
    "compilerOptions": {
        "outDir": "./","noEmitOnError": true,"experimentalDecorators": true,"target": "ES2020","watch": true,},"exclude":  []  
}

我认为可能是错误地引发错误的转码的另一个原因是,如果我设置了noEmitOnError: false,那么当运行已编译的JS时,我会将预期结果记录到了控制台。

任何帮助将不胜感激。谢谢。

解决方法

您所缺少的是装饰器不会变异他们装饰的东西的类型。由于greet()的{​​{1}}方法被声明为Foo类型,因此即使您已经装饰了它,编译器仍会认为它是()=>void类型。这种修饰不会导致编译器将其变异为()=>void

在GitHub microsoft/TypeScript#4881中有一个未解决的问题,要求支持装饰器类型突变(问题标题讨论了有关类装饰器改变类类型的问题,但该问题也正在用于跟踪对方法装饰器的支持更改方法类型)。 主要实现此目标的绊脚石是TC39 proposal for adding decorators to JavaScript进入第二阶段已有好几年了。 TypeScript通常不喜欢在第3阶段之前实施提案,在第3阶段,功能的一般行为往往会被巩固。不幸的是,TC39提案与TypeScript的实施已发生重大变化。因此,在TC39提案确定下来并进入第3阶段之前,TypeScript可能不会对装饰器做任何进一步的处理。

目前,没有一个我很喜欢的解决方法。只需将装饰器用作普通函数,就可以很好地模拟类装饰器的变异。但是,方法装饰器的变异不容易以类型安全的方式进行模拟。因此,我们可能需要退回到type assertions或更糟的级别,以哄骗编译器接受您的工作。

例如,您可以做的一件事是使用单个overload来手动调整呼叫签名,使其与您期望的一致。如果实现与该呼叫签名不兼容,则可能还需要在呼叫签名之前添加//@ts-ignore comment来禁止它。它虽然不漂亮,但至少可以让您继续使用装饰器并继续生活:

(str: string)=>void

Playground link to code

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。