是否有办法在TypeScript中获得类的属性名称?
在示例中,我想"描述"类A
或任何类,并获得其属性的数组(也许只有public
的?),这是可能的吗?还是应该先实例化对象?
class A {
private a1;
private a2;
/** Getters and Setters */
}
class Describer<E> {
toBeDescribed:E ;
describe(): Array<string> {
/**
* Do something with 'toBeDescribed'
*/
return ['a1', 'a2']; //<- Example
}
}
let describer = new Describer<A>();
let x= describer.describe();
/** x should be ['a1', 'a2'] */
这个TypeScript代码
class A {
private a1;
public a2;
}
编译成以下JavaScript代码
class A {
}
这是因为JavaScript中的属性只有在它们具有一些值之后才开始存在。你必须给这些属性赋值。
class A {
private a1 = "";
public a2 = "";
}
编译成
class A {
constructor() {
this.a1 = "";
this.a2 = "";
}
}
但是,你不能仅仅从类中获得属性(你只能从原型中获得方法)。您必须创建一个实例。然后通过调用Object.getOwnPropertyNames()
获得属性。
let a = new A();
let array = return Object.getOwnPropertyNames(a);
array[0] === "a1";
array[1] === "a2";
应用于您的示例
class Describer {
static describe(instance): Array<string> {
return Object.getOwnPropertyNames(instance);
}
}
let a = new A();
let x = Describer.describe(a);
有些答案是部分错误的,其中的一些事实也是部分错误的。
回答你的问题:是的!你可以。
在打印稿class A {
private a1;
private a2;
}
在Javascript中生成以下代码:
var A = /** @class */ (function () {
function A() {
}
return A;
}());
正如@Erik_Cupal所说,你可以这样做:
let a = new A();
let array = return Object.getOwnPropertyNames(a);
但是这是不完整的。如果你的类有一个自定义构造函数会发生什么?你需要对Typescript做一个小技巧,因为它不会编译。你需要分配为any:
let className:any = A;
let a = new className();// the members will have value undefined
一般的解决方案是:
class A {
private a1;
private a2;
constructor(a1:number, a2:string){
this.a1 = a1;
this.a2 = a2;
}
}
class Describer{
describeClass( typeOfClass:any){
let a = new typeOfClass();
let array = Object.getOwnPropertyNames(a);
return array;//you can apply any filter here
}
}
为了更好地理解, 将根据上下文引用。
另一个解决方案,您可以像这样迭代对象键,注意:您必须使用具有现有属性的实例化对象:
printTypeNames<T>(obj: T) {
const objectKeys = Object.keys(obj) as Array<keyof T>;
for (let key of objectKeys)
{
console.log('key:' + key);
}
}
只是为了好玩
class A {
private a1 = void 0;
private a2 = void 0;
}
class B extends A {
private a3 = void 0;
private a4 = void 0;
}
class C extends B {
private a5 = void 0;
private a6 = void 0;
}
class Describer {
private static FRegEx = new RegExp(/(?:this.)(.+?(?= ))/g);
static describe(val: Function, parent = false): string[] {
var result = [];
if (parent) {
var proto = Object.getPrototypeOf(val.prototype);
if (proto) {
result = result.concat(this.describe(proto.constructor, parent));
}
}
result = result.concat(val.toString().match(this.FRegEx) || []);
return result;
}
}
console.log(Describer.describe(A)); // ["this.a1", "this.a2"]
console.log(Describer.describe(B)); // ["this.a3", "this.a4"]
console.log(Describer.describe(C, true)); // ["this.a1", ..., "this.a6"]
更新:如果你使用自定义构造函数,此功能将中断。
其他答案主要是获取对象的所有名称,要获取属性的值,可以使用yourObj[name]
,例如:
var propNames = Object.getOwnPropertyNames(yourObj);
propNames.forEach(
function(propName) {
console.log(
'name: ' + propName
+ ' value: ' + yourObj[propName]);
}
);
我目前正在为Typescript开发一个类似linq的库,并希望在Typescript/Javascript中实现类似c#的GetProperties的东西。我使用Typescript和泛型的次数越多,我就越清楚地认识到,通常必须有一个具有初始化属性的实例化对象,才能在运行时获得有关类属性的任何有用信息。但无论如何,从构造函数对象或对象数组中检索信息是很好的,并且要灵活。
这是我现在得到的结果。
首先,我定义Array原型方法(c#开发人员称之为"扩展方法")。
export { } //creating a module of below code
declare global {
interface Array<T> {
GetProperties<T>(TClass: Function, sortProps: boolean): string[];
} }
GetProperties方法是这样的,灵感来自madreason的回答。
if (!Array.prototype.GetProperties) {
Array.prototype.GetProperties = function <T>(TClass: any = null, sortProps: boolean = false): string[] {
if (TClass === null || TClass === undefined) {
if (this === null || this === undefined || this.length === 0) {
return []; //not possible to find out more information - return empty array
}
}
// debugger
if (TClass !== null && TClass !== undefined) {
if (this !== null && this !== undefined) {
if (this.length > 0) {
let knownProps: string[] = Describer.describe(this[0]).Where(x => x !== null && x !== undefined);
if (sortProps && knownProps !== null && knownProps !== undefined) {
knownProps = knownProps.OrderBy(p => p);
}
return knownProps;
}
if (TClass !== null && TClass !== undefined) {
let knownProps: string[] = Describer.describe(TClass).Where(x => x !== null && x !== undefined);
if (sortProps && knownProps !== null && knownProps !== undefined) {
knownProps = knownProps.OrderBy(p => p);
}
return knownProps;
}
}
}
return []; //give up..
}
}
描述符方法与madreason的答案大致相同。它可以处理类函数和如果你得到一个对象。然后它将使用Object。
如果没有给出类函数(即c#开发人员的类'type')。class Describer {
private static FRegEx = new RegExp(/(?:this.)(.+?(?= ))/g);
static describe(val: any, parent = false): string[] {
let isFunction = Object.prototype.toString.call(val) == '[object Function]';
if (isFunction) {
let result = [];
if (parent) {
var proto = Object.getPrototypeOf(val.prototype);
if (proto) {
result = result.concat(this.describe(proto.constructor, parent));
}
}
result = result.concat(val.toString().match(this.FRegEx));
result = result.Where(r => r !== null && r !== undefined);
return result;
}
else {
if (typeof val == "object") {
let knownProps: string[] = Object.getOwnPropertyNames(val);
return knownProps;
}
}
return val !== null ? [val.tostring()] : [];
}
}
这里你可以看到两个规范,用来测试Jasmine。
class Hero {
name: string;
gender: string;
age: number;
constructor(name: string = "", gender: string = "", age: number = 0) {
this.name = name;
this.gender = gender;
this.age = age;
}
}
class HeroWithAbility extends Hero {
ability: string;
constructor(ability: string = "") {
super();
this.ability = ability;
}
}
describe('Array Extensions tests for TsExtensions Linq esque library', () => {
it('can retrieve props for a class items of an array', () => {
let heroes: Hero[] = [<Hero>{ name: "Han Solo", age: 44, gender: "M" }, <Hero>{ name: "Leia", age: 29, gender: "F" }, <Hero>{ name: "Luke", age: 24, gender: "M" }, <Hero>{ name: "Lando", age: 47, gender: "M" }];
let foundProps = heroes.GetProperties(Hero, false);
//debugger
let expectedArrayOfProps = ["name", "age", "gender"];
expect(foundProps).toEqual(expectedArrayOfProps);
expect(heroes.GetProperties(Hero, true)).toEqual(["age", "gender", "name"]);
});
it('can retrieve props for a class only knowing its function', () => {
let heroes: Hero[] = [];
let foundProps = heroes.GetProperties(Hero, false);
let expectedArrayOfProps = ["this.name", "this.gender", "this.age"];
expect(foundProps).toEqual(expectedArrayOfProps);
let foundPropsThroughClassFunction = heroes.GetProperties(Hero, true);
//debugger
expect(foundPropsThroughClassFunction.SequenceEqual(["this.age", "this.gender", "this.name"])).toBe(true);
});
正如madreason所提到的,你必须初始化props以从类Function本身获取任何信息,否则当Typescript代码转换为Javascript代码时,它将被剥离。
Typescript 3.7在泛型方面做得很好,但是由于有c#和反射的背景,Typescript和泛型的一些基本部分仍然让人感觉有些松散和未完成的事情。就像我这里的代码一样,但至少我得到了我想要的信息——给定类或对象实例的属性名称列表。
SequenceEqual是这个方法:
if (!Array.prototype.SequenceEqual) {
Array.prototype.SequenceEqual = function <T>(compareArray: T): boolean {
if (!Array.isArray(this) || !Array.isArray(compareArray) || this.length !== compareArray.length)
return false;
var arr1 = this.concat().sort();
var arr2 = compareArray.concat().sort();
for (var i = 0; i < arr1.length; i++) {
if (arr1[i] !== arr2[i])
return false;
}
return true;
}
}
使用这些
export class TableColumns<T> {
constructor(private t: new () => T) {
var fields: string[] = Object.keys(new t())
console.log('fields', fields)
console.log('t', t)
}
}
使用columns_logs = new TableColumns<LogItem>(LogItem);
输出fields (12) ["id", "code", "source", "title", "deleted", "checked", "body", "json", "dt_insert", "dt_checked", "screenshot", "uid"]
js类
t class LogItem {
constructor() {
this.id = 0;
this.code = 0;
this.source = '';
this.title = '';
this.deleted = false;
this.checked = false;
…
这里还有另一个答案也符合作者的要求:'获取接口定义的所有属性名称的方法
如果你在你的类中使用ts-transformer-keys插件和一个接口,你可以得到这个类的所有键。
但是如果你正在使用Angular或React,那么在某些情况下,需要额外的配置(webpack和typescript)才能使其工作:https://github.com/kimamula/ts-transformer-keys/issues/4
到目前为止,答案需要实例化类并依赖Object.Keys
。有一种涉及@decorators
的方法没有被提及。
在类中,为想要描述的每个字段添加一个装饰器。下面是一个示例,我用SQL列类型注释每个字段。
class Table {
@field("VARCHAR(30)") lastName?: string;
@field("TIMESTAMP") dob?: number;
}
然后在类上调用getField返回一个数组,这些字段带有您想要的注释
> getFields(Table)
[{ fieldName: "lastName", columnType: "VARCHAR(30)"},
{ fieldName: "dob", columnType: "TIMESTAMP"}]
要做到这一点,您只需要创建两个函数。我在这里做了一个工作示例:https://codepen.io/paganaye/pen/bGvavoL?editors=0010
如果你喜欢,这里是代码
/* Original TypeScript Source
// ==== getFields method starts here ====
const FIELDS = Symbol("fields");
interface FieldDecorator {
fieldName: string;
columnType: string;
}
function field(columnType: string) {
function createDecoratorFunction(table: any, fieldName: any) {
let fieldDecorator: FieldDecorator = { fieldName, columnType };
(table[FIELDS] || (table[FIELDS] = [])).push(fieldDecorator);
return fieldDecorator as any;
}
return createDecoratorFunction;
}
function getFields(table: any): FieldDecorator[] {
return table.prototype[FIELDS];
}
// ==== getFields method ends here ====
// ==== sample code from here ====
class Table {
@field("VARCHAR(30)") lastName?: string;
@field("TIMESTAMP") dob?: number;
}
document.getElementById("output");
output.innerText = JSON.stringify(getFields(Table), undefined, " ");
*/
"use strict";
// ==== getFields method starts here ====
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
const FIELDS = Symbol("fields");
function field(columnType) {
function createDecoratorFunction(table, fieldName) {
let fieldDecorator = { fieldName, columnType };
(table[FIELDS] || (table[FIELDS] = [])).push(fieldDecorator);
return fieldDecorator;
}
return createDecoratorFunction;
}
function getFields(table) {
return table.prototype[FIELDS];
}
// ==== getFields method ends here ====
// ==== sample code from here ====
class Table {
}
__decorate([
field("VARCHAR(30)"),
__metadata("design:type", String)
], Table.prototype, "lastName", void 0);
__decorate([
field("TIMESTAMP"),
__metadata("design:type", Number)
], Table.prototype, "dob", void 0);
document.getElementById("output");
output.innerText = JSON.stringify(getFields(Table), undefined, " ");
// [
// { fieldName:"lastName", columnType:"VARCHAR(30)"},
// { fieldName:"dob", columnType:"TIMESTAMP"}
// ]
body {
padding: 2em;
}
pre {
background-color: #cce;
padding: 0.5em;
}
<p>Here we start from </p>
<pre>
class Table {
@field("VARCHAR(30)") lastName?: string;
@field("TIMESTAMP") dob?: number;
}
</pre>
<p>and we automatically get</p>
<pre id="output">
</pre>
<p>There is no library used you only need this in your tsconfig.json</p>
<pre>
"compilerOptions": {
"experimentalDecorators": true
}
</pre>
我知道没有人喜欢使用this
,但这里有一个很酷的例子:
class Account {
firstName: string;
lastName: string;
email: string;
constructor() {
console.log(Object.keys(this)); // ['firstName', 'lastName', 'email']
}
}