Typescript get class fields

[typescript] Get properties of a class

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

In the example, I would like to ‘describe’ the class A or any class and get an array of its properties (maybe only public ones?), is it possible? Or should I instantiate the object first?

class A < private a1; private a2; /** Getters and Setters */ >class Describer  < toBeDescribed:E ; describe(): Array < /** * Do something with 'toBeDescribed' */ return ['a1', 'a2']; //> let describer = new Describer(); let x= describer.describe(); /** x should be ['a1', 'a2'] */ 

This question is related to typescript reflection

The answer is

compiles to this JavaScript code

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

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() .

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 < return Object.getOwnPropertyNames(instance); >> let a = new A(); let x = Describer.describe(a); 

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

Answer your question: Yes! You can.

In Typescript

Generates the following code in Javascript:

var A = /** @class */ (function () < function A() < >return A; >()); 

as @Erik_Cupal said, you could just do:

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:

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

A general solution will be:

For better understanding this will reference depending on the context.

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

printTypeNames(obj: T) < const objectKeys = Object.keys(obj) as Array; 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"] 

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

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.

Here is what I ended up with for now.

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

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

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

if (!Array.prototype.GetProperties) < Array.prototype.GetProperties = function (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).

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.

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[] = [< name: "Han Solo", age: 44, gender: "M" >, < name: "Leia", age: 29, gender: "F" >, < name: "Luke", age: 24, gender: "M" >, < 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.

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.

SequenceEqual is this method btw:

 if (!Array.prototype.SequenceEqual) < Array.prototype.SequenceEqual = function (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; > > 

Источник

How to get class fields of a given type?

I have a class with several methods that make requests to the API. I would like to be able to display a loading indicator for each of these calls independently. I don’t want to add new class field for each loading status indication property, and use one big object instead. The question is: what type should I use for isLoading in my class definition?

export class LibraryStore  authors: Author[] = []; books: Book[] = []; isLoading: any /* ? */ = <>; fetchAuthors()  this.isLoading.fetchAuthors = true; try  // . > catch (e)  // . > finally  this.isLoading.fetchAuthors = false; > > fetchBooks()  // . > > 

The Solution

The easiest and most obvious way to make it work is to use Record . But this type allows any string, not only methods’ names. Let’s find out how to restrict this.

Step 1. KeyOfType

export type KeyOfTypeType, ValueType> = keyof  [Key in keyof Type as Type[Key] extends ValueType ? Key : never]: any; >; 

The above code shows a generic type that extracts keys from Type , where TypeTypescript get class fields is of type ValueType or a derivative thereof.

class SomeObject  a = 1; b = 'foo'; c = 'bar'; d = true; > const foobar: KeyOfTypeSomeObject, string>; // 'b' | 'c' 

And that’s it. KeyOfType is an answer to the question in title. But let’s take it a little bit further.

Step 2. ClassMethods

export type ClassMethodsT> = KeyOfTypeT, Function>; 

Step 3. IsLoadingRecord

export type IsLoadingRecordType> = PartialRecordClassMethodsOmitType, 'isLoading'>>, boolean>>; 
export class LibraryStore  authors: Author[] = []; books: Book[] = []; isLoading: IsLoadingRecordLibraryStore> = <>; fetchAuthors()  this.isLoading.fetchAuthors = true; try  // . > catch (e)  // . > finally  this.isLoading.fetchAuthors = false; > > fetchBooks()  // . > > 
ClassMethodsOmitType, 'isLoading'>> 

ClassMethods type returns all methods from Type , except for isLoading . You may ask, why? If isLoading is not omitted, using it inside Type (like in example above) would end with TypeScript error:

TS2502: 'isLoading' is referenced directly or indirectly in its own type annotation. 
RecordClassMethodsOmitType, 'isLoading'>>, boolean> 
PartialRecordClassMethodsOmitType, 'isLoading'>>, boolean>> 
Autocomplete hints
Code editor shows proper autocomplete hints

The End

Hope you enjoyed this quick journey. If you have any problem I could help you with, please leave a comment. See you next time!

Источник

[typescript] Get properties of a class

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

In the example, I would like to ‘describe’ the class A or any class and get an array of its properties (maybe only public ones?), is it possible? Or should I instantiate the object first?

class A < private a1; private a2; /** Getters and Setters */ >class Describer  < toBeDescribed:E ; describe(): Array < /** * Do something with 'toBeDescribed' */ return ['a1', 'a2']; //> let describer = new Describer(); let x= describer.describe(); /** x should be ['a1', 'a2'] */ 

This question is related to typescript reflection

The answer is

compiles to this JavaScript code

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

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() .

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 < return Object.getOwnPropertyNames(instance); >> let a = new A(); let x = Describer.describe(a); 

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

Answer your question: Yes! You can.

In Typescript

Generates the following code in Javascript:

var A = /** @class */ (function () < function A() < >return A; >()); 

as @Erik_Cupal said, you could just do:

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:

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

A general solution will be:

For better understanding this will reference depending on the context.

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

printTypeNames(obj: T) < const objectKeys = Object.keys(obj) as Array; 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"] 

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

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.

Here is what I ended up with for now.

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

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

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

if (!Array.prototype.GetProperties) < Array.prototype.GetProperties = function (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).

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.

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[] = [< name: "Han Solo", age: 44, gender: "M" >, < name: "Leia", age: 29, gender: "F" >, < name: "Luke", age: 24, gender: "M" >, < 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.

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.

SequenceEqual is this method btw:

 if (!Array.prototype.SequenceEqual) < Array.prototype.SequenceEqual = function (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; > > 

Источник

Читайте также:  Find all java process linux
Оцените статью