像在 C# 中一样,在 TypeScript 中一次为反射元数据传递对象和属性
Pass object and attribute at once for reflect-metadata in TypeScript like in C#
在 C# 中,我们使用 DataAnnotation 在属性上添加元属性。我需要 TypeScript 中的此功能用于 ldap 模型 class。装饰器应该设置在 LDAP 目录中内部使用的 LDAP 属性
export class LdapUser {
@ldapAttribute('sn')
sureName: string;
}
export function ldapAttribute(attributeName: string): (target: any, propertyKey: string) => void {
return function (target: any, propertyKey: string) {
Reflect.defineMetadata("ldapAttribute", attributeName, target, propertyKey);
}
}
但是要获取 ldapAttribute
装饰器值,我需要将对象和属性名称作为原始字符串传递,如下所示:
let user = new LdapUser();
let sureNameAttribute = Reflect.getMetadata("ldapAttribute", user, "sureName"); // sn
它有效,但这似乎是不好的做法,因为当 sureName
属性在 LdapUser
中重命名而不将其应用于 Reflect.getMetadata()
时,它会导致运行时而不是编译器错误称呼。而且还缺少智能感知。所以我正在寻找这样的解决方案:
let sureNameAttribute = Reflect.getMetadata("ldapAttribute", user.sureName);
这里的问题是我需要某种反射来划分 user.SureName
属性名称(此处 sureName
)和 class 对象(此处 user
)。我已经在 C# 中使用反射做了类似的事情,但不知道如何在 TS 中做到这一点。
解决方法
它不如在 C# 中使用反射好,但比只使用纯字符串要好:
export function getLdapAttribute<T>(instance: T, attributeName: keyof T) : string {
let value : string = Reflect.getMetadata("ldapAttribute", instance, attributeName);
return value;
}
用法
let attributeValue = getLdapAttribute(user, "sureName"); // cn
遗憾的是我们这里没有智能感知。但至少如果属性名称不存在,我们会得到一个编译器错误。
我们使用下一个方法来解决这个问题:
export type FieldSpec<TModel, TResult> = ((model?: TModel) => TResult) | string;
export function getFieldName<TModel, TResult>(fieldSpec: FieldSpec<TModel, TResult>): string {
if (typeof (fieldSpec) == "string" || !fieldSpec) {
return fieldSpec as string;
} else {
var fullFunc = fieldSpec.toString();
var parts = fullFunc.split(/[.;}]/).filter(x => x.trim().length > 0);
return parts[parts.length - 1].trim();
}
}
有了这些助手,您可以编写:
export function getLdapAttribute<T>(instance: T, attributeName: FieldSpec<T,any>) : string {
let value : string = Reflect.getMetadata("ldapAttribute", instance, getFieldName(attributeName));
return value;
}
let user = new LdapUser();
let sureNameAttribute = getLdapAttribute(user, () => user.sureName);
在 C# 中,我们使用 DataAnnotation 在属性上添加元属性。我需要 TypeScript 中的此功能用于 ldap 模型 class。装饰器应该设置在 LDAP 目录中内部使用的 LDAP 属性
export class LdapUser {
@ldapAttribute('sn')
sureName: string;
}
export function ldapAttribute(attributeName: string): (target: any, propertyKey: string) => void {
return function (target: any, propertyKey: string) {
Reflect.defineMetadata("ldapAttribute", attributeName, target, propertyKey);
}
}
但是要获取 ldapAttribute
装饰器值,我需要将对象和属性名称作为原始字符串传递,如下所示:
let user = new LdapUser();
let sureNameAttribute = Reflect.getMetadata("ldapAttribute", user, "sureName"); // sn
它有效,但这似乎是不好的做法,因为当 sureName
属性在 LdapUser
中重命名而不将其应用于 Reflect.getMetadata()
时,它会导致运行时而不是编译器错误称呼。而且还缺少智能感知。所以我正在寻找这样的解决方案:
let sureNameAttribute = Reflect.getMetadata("ldapAttribute", user.sureName);
这里的问题是我需要某种反射来划分 user.SureName
属性名称(此处 sureName
)和 class 对象(此处 user
)。我已经在 C# 中使用反射做了类似的事情,但不知道如何在 TS 中做到这一点。
解决方法
它不如在 C# 中使用反射好,但比只使用纯字符串要好:
export function getLdapAttribute<T>(instance: T, attributeName: keyof T) : string {
let value : string = Reflect.getMetadata("ldapAttribute", instance, attributeName);
return value;
}
用法
let attributeValue = getLdapAttribute(user, "sureName"); // cn
遗憾的是我们这里没有智能感知。但至少如果属性名称不存在,我们会得到一个编译器错误。
我们使用下一个方法来解决这个问题:
export type FieldSpec<TModel, TResult> = ((model?: TModel) => TResult) | string;
export function getFieldName<TModel, TResult>(fieldSpec: FieldSpec<TModel, TResult>): string {
if (typeof (fieldSpec) == "string" || !fieldSpec) {
return fieldSpec as string;
} else {
var fullFunc = fieldSpec.toString();
var parts = fullFunc.split(/[.;}]/).filter(x => x.trim().length > 0);
return parts[parts.length - 1].trim();
}
}
有了这些助手,您可以编写:
export function getLdapAttribute<T>(instance: T, attributeName: FieldSpec<T,any>) : string {
let value : string = Reflect.getMetadata("ldapAttribute", instance, getFieldName(attributeName));
return value;
}
let user = new LdapUser();
let sureNameAttribute = getLdapAttribute(user, () => user.sureName);