Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript looping through class type properties

How can one loop through the properties of a class in TypeScript? Take the following class for example:

export class Task implements Itask {
  public Id: number = 0;
  public Name: string;
  public Description: string;
  public Completed: boolean = false;
  public TaskType: TaskType;
}

Im want to retrieve the properties, hence: ["Id", Name", "Description", "Completed", "TaskType"]

Tried

GetTaskHeaders = () => {
  const _self = this;
  const tHead = $('<thead />').append('<tr />');

  for (let i = 0; typeof TodoApp.Task.arguments; i++) {
    const th = $('<th />');
    th.append(TodoApp.Task.arguments[i]);
    tHead.append(th);
  }

  console.log(tHead);

  return tHead;
};  

Unfortunately without success, i know using "TodoApp.Task.arguments" is incorrect. However, can someone show me the right way please?

like image 486
meji Avatar asked Jun 19 '15 18:06

meji


People also ask

How to loop through properties of an object TypeScript?

If you want to iterate over the keys and values in an object, use either a keyof declaration ( let k: keyof T ) or Object. entries . The former is appropriate for constants or other situations where you know that the object won't have additional keys and you want precise types.

How do you iterate through an object in angular TypeScript?

To iterate over object in Angular, we can use the keyvalue pipe. to loop through the entries in myObject by using the keyvalue pipe with *ngFor . Then we get the key's value from item.

What is a class property in TypeScript?

TypeScript supports object-oriented programming features like classes, interfaces, etc. A class in terms of OOP is a blueprint for creating objects. A class encapsulates data for the object. Typescript gives built in support for this concept called class.


3 Answers

Let's consider that all "not defined" properties i.e. all properties that are defined in the typescript class like (I wrote "not defined" and not undefined for a reason that will be clear below)

class A { 

   prop1: string
   prop2: number

} 

will not be enumerated by any of Object.keys or this.hasOwnProperty(k) since the autogen javascript has no knowledge of those properties. You have only one option, when you create your typescript class, that is to initialize all properties to default values like

class A { 

   prop1: string
   prop2: number
   prop3: B

   constructor() {
     this.prop1="";
     this.prop2=-1;
     this.prop3=null;
   }

} 

At this point you will get all the properties of A instance like in this mapping iteration from a dictionary

var a = new A();
for (var i in properties) {
   if (a.hasOwnProperty(i)) {
      a[i]=properties[i];
   }
}

If you don't like the default values solution, you can still do this using the magic undefined javascript keyword so that you do:

class A { 

   prop1: string = undefined
   prop2: number = undefined

} 

At this point the javascript counterpart will have all properties in the model and you will iterate them with Object.keys(this) or inspect them via the this.hasOwnProperty

like image 171
loretoparisi Avatar answered Oct 15 '22 19:10

loretoparisi


See How do I loop through or enumerate a JavaScript object?

In your case, something like:

for (var i in TodoApp.Task) {
    if (TodoApp.Task.hasOwnProperty(i)) {
        var th = $('<th />').append(TodoApp.Task[i]);
        tHead.append(th);
    }
}
like image 34
Roy J Avatar answered Oct 15 '22 19:10

Roy J


Here is my answer, based on the question interpreted as in:

How to iterate during runtime on the enumeration of the (public) properties of a TypeScript declared class or other declared interface (or even object-type) ?

And the question could be varied into using private class members, given they would be accessible in either scope.

Answer: can't do. Unfortunately. The only thing you can do to get close to it is the following. And this will hopefully explain the ambiguity between the Q/As here, and what some TS devs might really want to do:

(Check/run this code below on the TS playground here)

interface IFoo {
   firstProp: number
   secondProp: number
}

class Foo implements IFoo {
    readonly firstProp = 100;
    readonly secondProp = 200;
    someOtherProp = "bar";
}

enum IFooProps {
  firstProp,
  secondProp
}

for (key in Object.keys(IFooProps)) { // or at best IFoo, not needing to use an enum
  // ... do something with interface keys
}

Note there isn't any way to check at compile-time so far IFooProps and IFoo actually match (TS feature request...).

And also, the above doesn't work so well, because keys of the enum object also includes its values ...

However, now to fix that, we could do:

const foo = new Foo();
Object.keys(IFooProps).forEach(prop => {
  if (!isNaN(Number(prop))) return;
  console.log(foo[prop]) // prints value of all IFoo implementations
});

// OR (this is just a "nicer" way to effectively run the same code as above here ^)

// can only be done with "runtime alive" objects (enums are, but class/interface are types not objects, 
// but enums are also types too ;))
function enumKeys<O extends object, K extends keyof O = keyof O>(obj: O): K[] {
    return Object.keys(obj).filter(k => Number.isNaN(+k)) as K[];
}

enumKeys(IFooProps).forEach(prop => {
    console.log(foo[prop]) 
})

If TypeScript would allow to check that IFooProps actually enumerates the properties of IFoo (or the compiler would allow to declare/generate such an enum from any declared interface, explicitly or implicitly), then this would actually be type-safe (and convenient).

At the moment, this is rather a clumsy workaround, needing to declare the interface "twice" (redundantly in the enum).

Some credit to: https://www.petermorlion.com/iterating-a-typescript-enum/ (For helping out on the enumKeys helper)

EDIT:

Another possible interpretation of this question (or to make sure to clarify with respect to what we said above):

Using enumerations of interface keys at compile-time (only)

Yes it is possible to enumerate on interfaces, BUT not at runtime:

(run this in TS playground here)

interface IBar {
  firstThing: [string, string]
  secondThing: [number, number]
}

function doThatWithTypedThing<Key extends keyof IBar>(someDeclaredThingKey: Key, someDeclaredThing: IBar[Key]) {
  console.log(someDeclaredThingKey, someDeclaredThing)
}

const bar: IBar = {
  firstThing: ["bli", "bla"],
  secondThing: [400, 500]
}

doThatWithTypedThing("firstThing", bar.firstThing) // prints: firstThing [ "bli", "bla" ]
doThatWithTypedThing("secondThing", bar.secondThing) // prints: secondThing [ 400, 500 ]
doThatWithTypedThing("firstThing", bar.secondThing) // <- compiler error 2345
doThatWithTypedThing("notFirstThing", bar.secondThing) // <- other compiler error 2345
like image 27
Tchakabam Avatar answered Oct 15 '22 18:10

Tchakabam