装饰器字段不能在 Super class 中使用 TypeScript 工作?
decorator field not working in supper class with TypeScript?
在文件中 base.ts
export const INCLUDED_FIELDS = new Map<string, string[]>();
export function JsonInclude(cls: any, name: string){
let clsName = cls.constructor.name;
let list: string[];
if (INCLUDED_FIELDS.has(clsName)) {
list = INCLUDED_FIELDS.get(clsName);
} else {
list = [];
INCLUDED_FIELDS.set(clsName, list);
}
list.push(name);
}
export default class Base{
@JsonInclude
public name: string;
toJSON(): { [name: string]: any } {
let json = {};
let include = INCLUDED_FIELDS.get(this.constructor.name);
cc.log(include);
Object.getOwnPropertyNames(this).filter(name => include.indexOf(name) > 0).forEach(name => {
cc.log("adding " + name);
json[name] = this[name];
});
return json;
}
}
并且在staff.ts
export default class Staff extends Base {
@JsonInclude
public salary: number = 1000;
@JsonInclude
public level: number = 1;
@JsonInclude
public comment:string = "";
}
似乎 Base
class 中的 name
字段不会添加到 INCLUDED_FIELDS
列表中?
那是因为装饰器不被继承。当 class 被定义而不是被实例化时,装饰器被调用。 Class 继承比复制粘贴更复杂,如果你仔细研究它,你就会知道你还有 super
调用,它允许你访问这样的继承 class (证明base class 真的不仅仅是在编译过程中硬粘贴)
也就是说,当继承 class 时,这样的 class 已经声明了 。因此,装饰器不会触发 - 它根本无法触发。
你可以在这个例子中很好地看到它:https://stackblitz.com/edit/ts-decorator-inheritance
“Base
class 中的 name
字段似乎不会添加到 INCLUDED_FIELDS
列表中”。实际上,name
将被添加到映射中,但它存储在其他键名中。你的地图看起来像
Map {
'Base' => [ 'name' ],
'Staff' => [ 'salary', 'level', 'comment' ],
}
对于 Base
class,您为 name
字段应用 JsonInclude
装饰器。现在 JsonInclude
- cls
的第一个参数将是 Base
,这意味着 cls.constructor.name
将 return "Base"
这是第一个键名地图。
同样的情况Staff
class,cls.constructor.name
会return"Staff"
最后,您在 Staff
class 的实例上调用 toJSON
:
const staff = new Staff();
console.log(staff.toJSON());
this
context in line let include = INCLUDED_FIELDS.get(this.constructor.name);
is Staff
instance (this.constructor.name
return "Staff"
), 那么你就得到 'Staff' => [ 'salary', 'level', 'comment' ]
地图的价值。
解决方法,一个“class类型”只使用一个键名。这是一个函数什么 return class class 的“根”名称 class.
function getRootClassName(cls: any): string {
let clsName = cls.constructor.name;
let parentCls = Object.getPrototypeOf(cls);
while (parentCls.constructor.name !== "Object") {
clsName = parentCls.constructor.name;
parentCls = Object.getPrototypeOf(parentCls);
}
return clsName;
}
示例:Object->Base->Staff ===> 该函数将为“Base”实例或“Staff”实例return“Base”字符串。
现在,您的装饰器将像:
export default function JsonInclude(cls: any, name: string) {
let clsName = getRootClassName(cls);
let list: string[];
if (INCLUDED_FIELDS.has(clsName)) {
list = INCLUDED_FIELDS.get(clsName);
} else {
list = [];
INCLUDED_FIELDS.set(clsName, list);
}
list.push(name);
}
和 toJSON
函数将类似于:
toJSON(): { [name: string]: any } {
let json: any = {};
let include = INCLUDED_FIELDS.get(getRootClassName(this)) || []; // get key name
for (const name in this) {
if (include.indexOf(name) >= 0) { // === 0 for the first item of include
console.log("adding " + name);
json[name] = (this as any)[name];
}
}
return json;
}
我使用 for...in
而不是 getOwnPropertyNames
,因为 getOwnPropertyNames
只是 return 直接在给定对象(而不是来自父对象)中找到的所有属性
最终代码:
base.ts
export const INCLUDED_FIELDS = new Map<string, string[]>();
function getRootClassName(cls: any): string {
let clsName = cls.constructor.name;
let parentCls = Object.getPrototypeOf(cls);
while (parentCls.constructor.name !== "Object") {
clsName = parentCls.constructor.name;
parentCls = Object.getPrototypeOf(parentCls);
}
return clsName;
}
export default function JsonInclude(cls: any, name: string) {
let clsName = getRootClassName(cls);
let list: string[];
if (INCLUDED_FIELDS.has(clsName)) {
list = INCLUDED_FIELDS.get(clsName);
} else {
list = [];
INCLUDED_FIELDS.set(clsName, list);
}
list.push(name);
}
export default class Base {
@JsonInclude
public name: string = '';
toJSON(): { [name: string]: any } {
let json: any = {};
let include = INCLUDED_FIELDS.get(getRootClassName(this)) || [];
for (const name in this) {
if (include.indexOf(name) >= 0) {
console.log("adding " + name);
json[name] = (this as any)[name];
}
}
return json;
}
}
staff.ts
export default class Staff extends Base {
@JsonInclude
public salary: number = 1000;
@JsonInclude
public level: number = 1;
@JsonInclude
public comment: string = "";
}
在文件中 base.ts
export const INCLUDED_FIELDS = new Map<string, string[]>();
export function JsonInclude(cls: any, name: string){
let clsName = cls.constructor.name;
let list: string[];
if (INCLUDED_FIELDS.has(clsName)) {
list = INCLUDED_FIELDS.get(clsName);
} else {
list = [];
INCLUDED_FIELDS.set(clsName, list);
}
list.push(name);
}
export default class Base{
@JsonInclude
public name: string;
toJSON(): { [name: string]: any } {
let json = {};
let include = INCLUDED_FIELDS.get(this.constructor.name);
cc.log(include);
Object.getOwnPropertyNames(this).filter(name => include.indexOf(name) > 0).forEach(name => {
cc.log("adding " + name);
json[name] = this[name];
});
return json;
}
}
并且在staff.ts
export default class Staff extends Base {
@JsonInclude
public salary: number = 1000;
@JsonInclude
public level: number = 1;
@JsonInclude
public comment:string = "";
}
似乎 Base
class 中的 name
字段不会添加到 INCLUDED_FIELDS
列表中?
那是因为装饰器不被继承。当 class 被定义而不是被实例化时,装饰器被调用。 Class 继承比复制粘贴更复杂,如果你仔细研究它,你就会知道你还有 super
调用,它允许你访问这样的继承 class (证明base class 真的不仅仅是在编译过程中硬粘贴)
也就是说,当继承 class 时,这样的 class 已经声明了 。因此,装饰器不会触发 - 它根本无法触发。
你可以在这个例子中很好地看到它:https://stackblitz.com/edit/ts-decorator-inheritance
“Base
class 中的 name
字段似乎不会添加到 INCLUDED_FIELDS
列表中”。实际上,name
将被添加到映射中,但它存储在其他键名中。你的地图看起来像
Map {
'Base' => [ 'name' ],
'Staff' => [ 'salary', 'level', 'comment' ],
}
对于 Base
class,您为 name
字段应用 JsonInclude
装饰器。现在 JsonInclude
- cls
的第一个参数将是 Base
,这意味着 cls.constructor.name
将 return "Base"
这是第一个键名地图。
同样的情况Staff
class,cls.constructor.name
会return"Staff"
最后,您在 Staff
class 的实例上调用 toJSON
:
const staff = new Staff();
console.log(staff.toJSON());
this
context in line let include = INCLUDED_FIELDS.get(this.constructor.name);
is Staff
instance (this.constructor.name
return "Staff"
), 那么你就得到 'Staff' => [ 'salary', 'level', 'comment' ]
地图的价值。
解决方法,一个“class类型”只使用一个键名。这是一个函数什么 return class class 的“根”名称 class.
function getRootClassName(cls: any): string {
let clsName = cls.constructor.name;
let parentCls = Object.getPrototypeOf(cls);
while (parentCls.constructor.name !== "Object") {
clsName = parentCls.constructor.name;
parentCls = Object.getPrototypeOf(parentCls);
}
return clsName;
}
示例:Object->Base->Staff ===> 该函数将为“Base”实例或“Staff”实例return“Base”字符串。
现在,您的装饰器将像:
export default function JsonInclude(cls: any, name: string) {
let clsName = getRootClassName(cls);
let list: string[];
if (INCLUDED_FIELDS.has(clsName)) {
list = INCLUDED_FIELDS.get(clsName);
} else {
list = [];
INCLUDED_FIELDS.set(clsName, list);
}
list.push(name);
}
和 toJSON
函数将类似于:
toJSON(): { [name: string]: any } {
let json: any = {};
let include = INCLUDED_FIELDS.get(getRootClassName(this)) || []; // get key name
for (const name in this) {
if (include.indexOf(name) >= 0) { // === 0 for the first item of include
console.log("adding " + name);
json[name] = (this as any)[name];
}
}
return json;
}
我使用 for...in
而不是 getOwnPropertyNames
,因为 getOwnPropertyNames
只是 return 直接在给定对象(而不是来自父对象)中找到的所有属性
最终代码:
base.ts
export const INCLUDED_FIELDS = new Map<string, string[]>();
function getRootClassName(cls: any): string {
let clsName = cls.constructor.name;
let parentCls = Object.getPrototypeOf(cls);
while (parentCls.constructor.name !== "Object") {
clsName = parentCls.constructor.name;
parentCls = Object.getPrototypeOf(parentCls);
}
return clsName;
}
export default function JsonInclude(cls: any, name: string) {
let clsName = getRootClassName(cls);
let list: string[];
if (INCLUDED_FIELDS.has(clsName)) {
list = INCLUDED_FIELDS.get(clsName);
} else {
list = [];
INCLUDED_FIELDS.set(clsName, list);
}
list.push(name);
}
export default class Base {
@JsonInclude
public name: string = '';
toJSON(): { [name: string]: any } {
let json: any = {};
let include = INCLUDED_FIELDS.get(getRootClassName(this)) || [];
for (const name in this) {
if (include.indexOf(name) >= 0) {
console.log("adding " + name);
json[name] = (this as any)[name];
}
}
return json;
}
}
staff.ts
export default class Staff extends Base {
@JsonInclude
public salary: number = 1000;
@JsonInclude
public level: number = 1;
@JsonInclude
public comment: string = "";
}