🔎 搜索词
"decorator" "field decorator" "side effect" "decorator side effect"
🕗 版本与回归信息
- 在版本5.0和5.2之间发生了变化
- 在我尝试的每个版本中,我都检查了关于装饰器的FAQ条目
⏯ Playground链接
https://www.typescriptlang.org/play?#code/MYewdgzgLgBAYgSwKYBsAmARJoBOBDKEHGAXhgAoo8cBzJKALhgFcw0kAzBMJNAGhigwUJAA9GMAMIo8ECIlSZsRAkUngR4gJSkAfDADeAXwBQJ4DLkwACtSTDDJmIPDQczYIRzlqNAIxMbtw0Ar4ATIFQOMGhtADMkdFgNDoGTs4wAPSZMAACUBAAtAg0YERI6c5QABYIEAB0AA44II1+pDC+fpVZOflFJWU4FRkwNXVNLY1hHeE92XkFxaXlPeMNza1xs-Hpps6bbQCEicFmB1NhJzBByecwh3HXtzQmpuaWEFK16DBiImwvrZhg40hdWn5nlEzulDldTnd0rkFOgsLhVDhYVMngjXu8hNBOh0eAB3b4IdDkADkfj8VIEVLCYXpMCpcTiVK05lcIBQSHqKBANB8XIWeEmENIJDIrHYXB4aF6MAA6gAVOAmMUS6ZSsiM5lKgDyAGlNTlxY9dSw2JxuLwlWqNUA
💻 代码
const FieldDecorator = (target: undefined, context: ClassFieldDecoratorContext) => {} // do nothing
class Parent {
constructor(arg1: string, arg2: string, arg3: string) {
// @ts-ignore
this.prop1 = arg1
// @ts-ignore
this.prop2 = arg2
// @ts-ignore
this.prop3 = arg3
}
}
class Child extends Parent {
prop1!: string
prop2!: string
@FieldDecorator
prop3!: string
}
const a = new Child('11', '22', '33')
console.log(a)
// a.prop1 === undefined // WTF
// a.prop2 === '22' // OK
// a.prop3 === undefined // WTF
🙁 实际行为
尽管在构造函数中给定了一个值,但被装饰的属性和第一个属性仍然是 undefined
🙂 预期行为
a
对象应该等于以下 obj
对象
const obj = {
prop1: "11"
prop2: "22"
prop3: "33"
}
9条答案
按热度按时间4ngedf3f1#
这三个属性都应该是
undefined
,因为根据ES规范,在派生类中声明的类字段是在基类构造函数运行之后初始化的。prop2
看起来像是一个bug。fivyi3re2#
@fatcerberus 你是说,即使没有装饰器,所有的字段都应该是
undefined
吗?这听起来很荒谬。这也是一个bug吗?https://www.typescriptlang.org/play?#code/MYGwhgzhAEAKYCcCmA7ALtA3gKGtYA9ihGggK7BoEIAUiA5gIwBc0JCAlivQDTQMAmVuy69+CegGZhpUQEosuPNAD0K6AAE0EALQd6KakiV40ACw4QAdAAcEBG42gBecUxOr1W3fsPIP5pa29jYCLm4CHmqa2noGRgEW1nYOkuEMkkoAvtg52KCQMADCFiAAJtBIAB5oqGUw8MjoingpjgCEMpzc2EptAp1ssj19IZKDIj15hMQYYOEoSADu0CUc5TQA5IyMm3ybAgJ70JuSkpty+UQQBCBIViAE9HSX0WDBDk7O3yc7m57QABGZHoAH5sG8PqEXD8DkcAcCwRD1O82mlvq5TucESDwUA
o3imoua43#
您正在使用现代装饰器与遗留类字段 "set" 行为相结合。
使用
useDefineForClassFields: true
(或者将target
更改为es2020
,稍后在 playground 中无法使用,但在本地使用tsc
可以正常工作)会带来由 wisecerberus 描述的现代行为https://www.typescriptlang.org/play?useDefineForClassFields=true#code/MYGwhgzhAEAKYCcCmA7ALtA3gKGtYA9ihGggK7BoEIAUiA5gIwBc0JCAlivQDTQMAmVuy69+CegGZhpUQEosuPNAD0K6AAE0EALQd6KakiV40ACw4QAdAAcEBG42gBecUxOr1W3fsPIP5pa29jYCLm4CHmqa2noGRgEW1nYOkuEMkkoAvtg52KCQMADCFiAAJtBIAB5oqGUw8MjoingpjgCEMpzc2EptAp1ssj19IZKDIj15hMQYYOEoSADu0CUc5TQA5IyMm3ybAgJ70JuSkpty+UQQBCBIViAE9HSX0WDBDk7O3yc7m57QABGZHoAH5sG8PqEXD8DkcAcCwRD1O82mlvq5TucESDwUA
我认为最好使用现代定义语义与现代装饰器相结合,或使用旧的 set 语义与实验性的(遗留)装饰器相结合,但不要将它们混合在一起。
llmtgqce4#
@IllusionMH 感谢澄清。这比我预期的要复杂得多。想要用新的装饰器来取乐,结果却变成了折磨。顺便说一下,如果“混合它们”是个坏主意,那么为什么它是默认配置?
tjvv9vkg5#
顺便说一下,如果"混合它们"是个坏主意,那么为什么它是默认配置?
之前的"默认"行为是你根本不能使用装饰器。一旦装饰器被标准化,你就可以在不选择的情况下使用它们。类字段行为也与先前的旧行为有关。
unguejic6#
你能试着用数字代替字符串吗?另外,试着用数组作为输入而不是三个单独的变量或行,看看这些输出是否会改变,以便进一步探索。
svdrlsy47#
这比我预期的要复杂得多。想要用新的装饰器玩乐的心情变成了折磨。
提示:确保始终使用
useDefineForClassFields: true
,这样一切都像普通的JavaScript一样工作(我喜欢为target
和module
使用esnext
,并使用测试来捕获我编写了过于新的语法的情况,然后调整我的手写代码并避免转译,仅在 TS 中使用类型注解)。在Chrome控制台中运行此代码显示定义语义:
顺便说一下,如果“混合它们”是个坏主意,那么为什么它是默认配置?
这是一个非常好的观点!
TypeScript 5允许使用标准装饰器,同时将
useDefineForClassFields
和false
保留为默认值(由于默认目标是超级旧的ES3目标,可能没有人需要默认使用这些功能),这导致人们编写了容易在纯JavaScript中出现问题的新装饰器代码(例如,如果有人复制/粘贴他们的装饰器到另一个纯JS项目中,或者有人从代码中去除类型以便在另一个纯JS项目中使用它,但它不起作用)。r7s23pms8#
看起来这个bug出现在我们在
useDefineForClassFields: false
中如何发出类字段,而装饰器存在的情况下。在#56955中,我们更新了我们的emit以匹配pzuraq/ecma262#12中对装饰器的规范性更改。为了确保装饰器和初始化器的正确时机,我们将字段初始化器与由字段装饰器添加的"额外"初始化器交错,通过将它们注入到以下字段的初始化器中。这里的bug是,即使
useDefineForClassFields
是false
,我们仍然会进行这种注入。结果是,这会向派生构造函数引入之前被省略的字段赋值。f0brbegy9#
这里的解决方法是使用"declare"声明类成员。
修复此问题的预计时间是什么时候?