typescript 获取类的属性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/40636292/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 07:28:43  来源:igfitidea点击:

Get properties of a class

typescriptreflection

提问by timmz

Is there a way to get properties names of class in TypeScript?

有没有办法在 TypeScript 中获取类的属性名称?

In the example, I would like to 'describe' the class Aor any class and get an array of its properties (maybe only publicones?), is it possible? Or should I instantiate the object first?

在示例中,我想“描述”类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'] */ 

回答by Erik Cupal

This TypeScript code

这个打字稿代码

class A {
    private a1;
    public a2;
}

compiles to this JavaScript code

编译为这个 JavaScript 代码

class A {
}

That's because properties in JavaScript start extisting only after they have some value. You have to assign the properties some value.

这是因为 JavaScript 中的属性只有在它们具有某些值后才开始存在。您必须为属性分配一些值。

class A {
    private a1 = "";
    public a2 = "";
}

it compiles to

它编译为

class A {
    constructor() {
        this.a1 = "";
        this.a2 = "";
    }
}

Still, you cannot get the properties from mere class (you can get only methods from prototype). You must create an instance. Then you get the properties by calling Object.getOwnPropertyNames().

尽管如此,您不能仅从类中获取属性(您只能从原型中获取方法)。您必须创建一个实例。然后你通过调用获取属性Object.getOwnPropertyNames()

let a = new A();
let array = return Object.getOwnPropertyNames(a);

array[0] === "a1";
array[1] === "a2";

Applied to your example

应用于您的示例

class Describer {
    static describe(instance): Array<string> {
        return Object.getOwnPropertyNames(instance);
    }
}

let a = new A();
let x = Describer.describe(a);

回答by titusfx

Some answers are partially wrong, and some facts in them are partially wrong as well.

有些答案部分错误,其中一些事实也部分错误。

Answer your question: Yes! You can.

回答你的问题:是的!你可以。

In Typescript

在打字稿中

class A {
    private a1;
    private a2;


}

Generates the following codein Javascript:

在 Javascript 中生成以下代码

var A = /** @class */ (function () {
    function A() {
    }
    return A;
}());

as @Erik_Cupal said, you could just do:

正如@Erik_Cupal 所说,你可以这样做:

let a = new A();
let array = return Object.getOwnPropertyNames(a);

But this is incomplete. What happens if your class has a custom constructor? You need to do a trick with Typescript because it will not compile. You need to assign as any:

但这是不完整的。如果你的类有一个自定义构造函数会发生什么?你需要用 Typescript 做一个技巧,因为它不会编译。您需要分配为任何:

let className:any = A;
let a = new className();// the members will have value undefined

A general solution will be:

一般的解决方案是:

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
   }
}

For better understanding thiswill reference depending on the context.

为了更好地理解,将根据上下文进行参考。

回答by johnny 5

Another solution, You can just iterate over the object keys like so, Note: you must use an instantiated object with existing properties:

另一种解决方案,您可以像这样迭代对象键, 注意:您必须使用具有现有属性的实例化对象

printTypeNames<T>(obj: T) {
    const objectKeys = Object.keys(obj) as Array<keyof T>;
    for (let key of objectKeys)
    {
       console.log('key:' + key);
    }
}

回答by madreason

Just for fun

只是为了好玩

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"]

Update: If you are using custom constructors, this functionality will break.

更新:如果您使用自定义构造函数,此功能将中断。

回答by Tore Aurstad

I am currently working on a Linq-like library for Typescript and wanted to implement something like GetProperties of C# in Typescript / Javascript. The more I work with Typescript and generics, the clearer picture I get of that you usually have to have an instantiated object with intialized properties to get any useful information out at runtime about properties of a class. But it would be nice to retrieve information anyways just from the constructor function object, or an array of objects and be flexible about this.

我目前正在为 Typescript 开发一个类似 Linq 的库,并希望在 Typescript / Javascript 中实现类似 GetProperties of C# 的东西。我使用 Typescript 和泛型的次数越多,我就越清楚,您通常必须拥有一个具有初始化属性的实例化对象,才能在运行时获取有关类属性的任何有用信息。但是无论如何都可以从构造函数对象或对象数组中检索信息并且对此保持灵活。

Here is what I ended up with for now.

这是我现在的结果。

First off, I define Array prototype method ('extension method' for you C# developers).

首先,我定义了 Array 原型方法(C# 开发人员的“扩展方法”)。

export { } //creating a module of below code
declare global {
  interface Array<T> {
    GetProperties<T>(TClass: Function, sortProps: boolean): string[];
} }

The GetProperties method then looks like this, inspired by madreason's answer.

受 madreason 的回答启发,GetProperties 方法看起来像这样。

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..
  }
}

The describer method is about the same as madreason's answer. It can handle both class Function and if you get an object instead. It will then use Object.getOwnPropertyNames if no class Function is given (i.e. the class 'type' for C# developers).

描述方法与 madreason 的答案大致相同。它可以处理类 Function 和如果你得到一个对象。如果没有给出类 Function(即 C# 开发人员的类“类型”),它将使用 Object.getOwnPropertyNames。

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()] : [];
  }
}

Here you see two specs for testing this out with Jasmine.

在这里,您可以看到两个使用 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);
  });

And as madreason mentioned, you have to initialize the props to get any information out from just the class Function itself, or else it is stripped away when Typescript code is turned into Javascript code.

正如 madreason 所提到的,你必须初始化 props 才能从类 Function 本身中获取任何信息,否则当 Typescript 代码变成 Javascript 代码时它会被剥离。

Typescript 3.7 is very good with Generics, but coming from a C# and Reflection background, some fundamental parts of Typescript and generics still feels somewhat loose and unfinished business. Like my code here, but at least I got out the information I wanted - a list of property names for a given class or instance of objects.

Typescript 3.7 非常适合泛型,但是来自 C# 和反射背景,Typescript 和泛型的一些基本部分仍然感觉有些松散和未完成的工作。就像我这里的代码一样,但至少我得到了我想要的信息 - 给定类或对象实例的属性名称列表。

SequenceEqual is this method btw:

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;
  }
}

回答by mdimai666

Use these

使用这些

export class TableColumns<T> {
   constructor(private t: new () => T) {
        var fields: string[] = Object.keys(new t())

        console.log('fields', fields)
        console.log('t', t)

    }
}

Usage

用法

columns_logs = new TableColumns<LogItem>(LogItem);

Output

输出

fields (12)?["id", "code", "source", "title", "deleted", "checked", "body", "json", "dt_insert", "dt_checked", "screenshot", "uid"]

js class

js类

t class LogItem {
constructor() {
    this.id = 0;
    this.code = 0;
    this.source = '';
    this.title = '';
    this.deleted = false;
    this.checked = false;
  …

回答by yu yang Jian

Other answers mainly get all name of object, to get value of property, you can use yourObj[name], for example:

其他答案主要是获取对象的所有名称,要获取属性的值,可以使用yourObj[name],例如:

var propNames = Object.getOwnPropertyNames(yourObj);
propNames.forEach(
    function(propName) {
        console.log(
           'name: ' + propName 
        + ' value: ' + yourObj[propName]);
    }
);