TypeScript, how to pass public field names with typed expression
我试图找出如何传递给定对象的属性名(或字段名)数组,而不使用所谓的魔术字符串-因为拼写错误很容易制作! 本质上,我在寻找与csharp的" Expression <>"有关的东西。
例如。 带有魔术弦:
例如。 键入,或者我想如何调用该函数:
作为参考,此函数看起来像这样:
带有魔术弦:(或我尝试输入的方式)
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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#中那样一个相关的"函数表达式"来提取被调用的属性,以获取其名称)
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
您无法摆脱字符串,打字稿中还没有诸如nameof属性之类的东西(尚未)。
但是,您可以做的是键入某些内容作为另一种类型的键。
像这样。
1 2 3 4 5 6 | interface Model { a: string, b: number } function searchFilter(model: Model, q: keyof Model) { } |
结果是:
1 2 3 | searchFilter(null, 'a') // works searchFilter(null, 'b') // works searchFilter(null, 'c') // error c is not a property of Model |
您可以输入如下类型的属性数组:
1 2 3 | function searchArray(model: Model, q: string, properties: Array<keyof Model>) { } searchArray(null, 'blabla', ['a', 'b']) |
我喜欢基于lambda的方法(但在足够/可能的情况下,大多数时候应该使用key):
1 2 3 4 5 6 7 8 9 10 | 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) |
可以创建与属性同名的闭包方法并调用所需的闭包方法:
1 2 3 4 5 6 7 8 9 10 11 12 | 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` |
相同的方法,但此处是
Nameof本机不可用,但已使用第三方库复制了该功能。
您可以通过使用第三方库(https://www.npmjs.com/package/ts-nameof)实现
在这种情况下,库根据您要使用的对象名称级别提供许多选项,例如变量本身的名称,方法的名称,方法的名称及其包含的类,等等。第四(摘自库文档)。
下面在左侧显示了已编译的JavaScript输出,在右侧显示了等效的TypeScript。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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); |
这些字符串在编译时会被替换,因此不应有任何运行时性能损失。
经过一些调试后,我确实找到了答案,但如有需要,请随时提供更好的答案。下面的代码和说明...:
由于您通过
一些警告!这不涉及嵌套的属性,其性能可能也不理想。对于我的用例来说,这足够了,但是欢迎任何想法或改进。现在,我将不再搜索-因为用例不需要它。
代码的要点在这里:
1 2 3 4 5 6 7 | 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()); }); |
在角度服务中实现,如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } } |
并在注入服务的组件的方法中这样使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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; } |
界面易于使用
1 2 3 | export interface IPropertySelector< T > { (x: T): any; } |