使用Typescript获取类的属性

时间:2016-11-16 15:45:27

标签: typescript reflection

有没有办法在TypeScript中获取类的属性名称:在示例中 我想描述一下' A类或任何类获取其属性数组(也许只有公共属性?),是否可能?或者我应该首先实例化对象?

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'] */ 

8 个答案:

答案 0 :(得分:27)

此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);

答案 1 :(得分:12)

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

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

在打字稿中

class A {
    private a1;
    private a2;


}

在Javascript中生成以下code

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

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

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

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

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

为了更好地理解将根据具体情况引用。

答案 2 :(得分:8)

只是为了好玩

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

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

答案 3 :(得分:7)

另一个解决方案,你可以像这样迭代对象键,你必须首先实例化对象:

class Signup(View):

    """ this class is used for user signup """

    def get(self, request):
        """ this function used to get the sign up form """
        form = UserCreationForm()
        return render(request, 'booking/signup.html', {'form': form})

    def post(self, request):
        """ this function used for post the sign up data """
        form = UserCreationForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('login')


class AuthLogin(View):

    """ Its for login """

    def get(self, request):
        """ this function used to get the login form """
        form = AuthenticationForm()
        return render(request, 'booking/login.html', {'form': form})

    def post(self, request):
        """ this function used for post the login data """
        form = AuthenticationForm(None, request.POST or None)
        if form.is_valid():
            login(request, form.get_user())
        return redirect('/')


def home(request):
    """ This is for displaying the home page """
    return render(request, 'booking/home.html', {})

答案 4 :(得分:2)

使用这些

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

答案 5 :(得分:1)

我目前正在为Typescript开发类似于Linq的库,并希望在Typescript / Javascript中实现C#的GetProperty。我与Typescript和泛型一起使用的越多,得到的画面越清晰,通常您必须具有带有初始化属性的实例化对象,才能在运行时获取有关类属性的任何有用信息。但是,无论如何仅从构造函数对象或对象数组中检索信息,并且对此灵活一点,将是很好的。

这是我现在最后要讨论的内容。

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

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

然后,在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..
  }
}

describer方法与madreason的答案大致相同。它既可以处理Function类,又可以处理对象。如果没有给出类Function(即C#开发人员的类'type'),它将使用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()] : [];
  }
}

在这里,您会看到两个规范,可以用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所提到的,您必须初始化道具以从类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;
  }
}

答案 6 :(得分:1)

这里还有一个也符合作者要求的答案:'compile-time' way to get all property names defined interface

如果您使用插件ts-transformer-keys和类的接口,则可以获取该类的所有键。

但是,如果您使用的是Angular或React,那么在某些情况下,还需要进行其他配置(webpack和typescript)才能使其正常工作:https://github.com/kimamula/ts-transformer-keys/issues/4

答案 7 :(得分:0)

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

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