我正在试图弄清楚如何传递给定对象的属性(或字段名)数组,而不使用所谓的魔术字符串 - 因为拼写错误很容易!本质上,我正在寻找与csharp的“Expression<>”相关的东西。我想。
E.g。用魔术字符串:
searchFilter(model, 'searchParameter', ['id', 'name'])
E.g。键入,或我想如何调用该函数:
searchFilter(model, 'searchParameter', [m => m.id, m => m.name])
作为参考,这个函数看起来有点像这样:
使用魔术字符串(或者我尝试键入的方式)
private searchFilter(mode: Model, q: string, properties: string[]): boolean {
if (q === '') return true;
q = q.trim().toLowerCase();
for (let property of properties) {
if (vacature[property.toString()].toString().toLowerCase().indexOf(q) >= 0) {
return true;
}
}
return false;
}
键入:(或者我是如何尝试键入的,但是这个当然只返回函数..我需要一个相关的'函数表达式',如在C#中提取被调用的属性,得到它的名字)
private searchFilter(mode: Model, q: string, propertySelector: ((x: Model) => any | string)[]): boolean {
if (q === '') return true;
q = q.trim().toLowerCase();
for (let property of propertySelector) {
if (vacature[property.toString()].toString().toLowerCase().indexOf(q) >= 0) {
return true;
}
}
return false;
}
答案 0 :(得分:2)
你无法摆脱字符串,因为在typescript(还)中没有 nameof 属性。
但你可以做的是输入某种东西作为另一种类型的钥匙。
喜欢这个。
interface Model {
a: string,
b: number
}
function searchFilter(model: Model, q: keyof Model) { }
这导致:
searchFilter(null, 'a') // works
searchFilter(null, 'b') // works
searchFilter(null, 'c') // error c is not a property of Model
您可以键入类似以下类型的属性数组:
function searchArray(model: Model, q: string, properties: Array<keyof Model>) { }
searchArray(null, 'blabla', ['a', 'b'])
答案 1 :(得分:1)
您可以通过使用第三方库(https://www.npmjs.com/package/ts-nameof)来实现nameof
功能。您可以在此处查看源代码:https://github.com/dsherret/ts-nameof
在这种情况下,库根据您要使用的对象名称级别提供了许多选项,例如变量本身的名称,方法的名称,方法的名称及其包含的类等等(摘自图书馆文档)。
下面显示左侧的已转换JavaScript输出和右侧的TypeScript等效项。
console.log("console"); // console.log(nameof(console));
console.log("log"); // console.log(nameof(console.log));
console.log("console.log"); // console.log(nameof.full(console.log));
console.log("alert.length"); // console.log(nameof.full(window.alert.length, 1));
console.log("length"); // console.log(nameof.full(window.alert.length, 2));
console.log("length"); // console.log(nameof.full(window.alert.length, -1));
console.log("alert.length"); // console.log(nameof.full(window.alert.length, -2));
console.log("window.alert.length"); // console.log(nameof.full(window.alert.length, -3));
"MyInterface"; // nameof<MyInterface>();
console.log("Array"); // console.log(nameof<Array<MyInterface>>());
"MyInnerInterface"; // nameof<MyNamespace.MyInnerInterface>();
"MyNamespace.MyInnerInterface"; // nameof.full<MyNamespace.MyInnerInterface>();
"MyInnerInterface"; // nameof.full<MyNamespace.MyInnerInterface>(1);
"Array"; // nameof.full<Array<MyInterface>>();
"prop"; // nameof<MyInterface>(o => o.prop);
这些字符串在转换时被替换,因此不应该有任何运行时性能损失。
答案 2 :(得分:1)
可以使用与属性相同的名称创建闭包方法,并调用所需的一个:
class Foo {
public bar: string = null; // property has to be initialized
}
function getPropertyName<T>(TCreator: { new(): T; }, expression: Function): string {
let obj = new TCreator();
Object.keys(obj).map(k => { obj[k] = () => k; });
return expression(obj)();
}
let result = getPropertyName(Foo, (o: Foo) => o.bar);
console.log(result); // Output: `bar`
同样的方法,但objects
而不是classes
是here
答案 3 :(得分:0)
经过一些调试,我确实找到了答案,但如果你有任何答案,请随时给出更好的答案。代码&amp;以下解释......:
由于您通过propertySelectors: ((x: T) => any | string)[]
传递了一系列函数,因此可以删除每个函数的主体。然后,您删除每个函数的return.
和;
部分,因此最终只得到属性名称。例如:
function (v) { v.id; }
.slice()
步骤之后变为v.id;
.slice()
步骤之后变为id
一些警告!这不包括嵌套属性,并且这种性能可能也不理想。 Hower for my usecase这已经足够了,但欢迎任何想法或改进。现在我不再搜索 - 因为我的用例不需要它。
代码的要点在这里:
let properties: string[] = [];
propertySelector.forEach(propertySelector => {
const functionBody = propertySelector.toString();
const expression = functionBody.slice(functionBody.indexOf('{') + 1, functionBody.lastIndexOf('}'));
const propertyName = expression.slice(expression.indexOf('.') + 1, expression.lastIndexOf(';'));
properties.push(propertyName.trim());
});
在角度服务中实现它看起来像这样:
import { Injectable } from '@angular/core';
import { IPropertySelector } from '../../models/property-selector.model';
@Injectable()
export class ObjectService {
extractPropertyNames<T>(propertySelectors: IPropertySelector<T>[]): string[] {
let propertyNames: string[] = [];
propertySelectors.forEach(propertySelector => {
const functionBody = propertySelector.toString();
const expression = functionBody.slice(functionBody.indexOf('{') + 1, functionBody.lastIndexOf('}'));
const propertyName = expression.slice(expression.indexOf('.') + 1, expression.lastIndexOf(';'));
propertyNames.push(propertyName);
});
return propertyNames;
}
}
并在注入服务的组件的方法中使用这样的方法:
private searchFilter(model: Model, q: string, propertySelectors: IPropertySelector<Model>[]): boolean {
if (q === '') return true;
q = q.trim().toLowerCase();
if (!this.cachedProperties) {
this.cachedProperties = this.objectService.extractPropertyNames(propertySelectors);
}
for (let property of this.cachedProperties) {
if (model[property].toString().toLowerCase().indexOf(q) >= 0) {
return true;
}
}
return false;
}
易于使用的界面
export interface IPropertySelector<T> {
(x: T): any;
}
答案 4 :(得分:0)
我喜欢基于lambda的方法(但在足够/可能的情况下,大多数时候应该使用密钥):
type valueOf<T> = T[keyof T];
function nameOf<T, V extends T[keyof T]>(f: (x: T) => V): valueOf<{ [K in keyof T]: T[K] extends V ? K : never }>;
function nameOf(f: (x: any) => any): keyof any {
var p = new Proxy({}, {
get: (target, key) => key
})
return f(p);
}
// Usage:
nameOf((vm: TModel) => vm.prop)