Reputation: 521
I'd like to create a generic TypeScript class for rendering (as a HTML list) of an array of objects which implement a specific interface
.
e.g.
class GenericListRenderer<T> {
items: T[];
constructor(listItems: T[], className?: string){
this.items = listItems;
...
}
private getPropertyNames(): string[]{
// What is the best way to access all property names defined in
// TypeScript interface 'T' that was used in this generic?
...
}
render(){
var propNames: string[] = this.getPropertyNames();
// render list with each item containing set of all
// key(prop name)/value pairs defined by interface 'T'
...
}
}
Q: what would be the best way to get a 'compile-time' list of all property names defined in the specified () TypeScript interface?
Like C++ templates, I believe that TypeScript could resolves these generics during "compile time", when TypeScript type information (like an interface supplied to the generic used to instantiate a particular object) is readily available.
Since all the required type information is potentially supplied, I was just curious if there was a TypeScript extension/facility available to access this info w/o excessive runtime filtering of 'vanilla' Javascript objects -- which might be problematic due to ambiguous inheritance issues (e.g. desired TypeScript inherited interface properties may get filtered out if runtime, generic, Javascript (obj.hasOwnProperty(prop)) is used for filtering properties).
This useful property introspection potential could be unambiguously resolved with TypeScript's super-set of type meta-data during 'compile-time' vs trying to resolve this information in the translated Javascript, when all of this type information is discarded.
I'd hate to 'reinvent-the wheel' with a potentially imperfect Javascript hack if a standard (TypeScript) approach exists.
Upvotes: 42
Views: 37961
Reputation: 944
Try SWC
compiler. You will be able to use Object.keys(new ClassName())
.
Also it is 20x faster than regular compiler
Upvotes: 0
Reputation: 1
You can try Accelatrix as it contains a JavaScript type introspection system that works at runtime:
https://www.nuget.org/packages/Accelatrix
https://www.npmjs.com/package/accelatrix
https://github.com/accelatrix/accelatrix
The type system of JavaScript is enhanced to include the four fundamental operations:
- GetHashCode()
- GetType()
- Equals()
- ToString()
You can now deal with classes in JavaScript at runtime as you would in C#, e.g.:
var myDog = new Bio.Mammal(8);
var myCat = new Bio.Feline(8, 9);
var timeIsSame = (new Date()).Equals(new Date()); //true
var areEqual = myDog.Equals(myCat); // false
var myCatType = myCat.GetType(); // Bio.Feline
var myCatBaseType = myCat.GetType().BaseType; // Bio.Mammal
var isAnimal = myCat.GetType().IsAssignableFrom(Bio.Animal); // true
var enums = Bio.TypesOfLocomotion.GetType(); // Accelatrix.EnumType
// sample classes:
export namespace Bio
{
export enum TypesOfLocomotion
{
Crawl,
Swim,
Walk,
Fly,
}
abstract class LivingBeing
{
public isExtinct = false;
}
export abstract class Eukaryotes extends LivingBeing
{
private locomotion: TypesOfLocomotion = null;
public get Locomotion(): TypesOfLocomotion
{
return this.locomotion;
}
public set Locomotion(value: TypesOfLocomotion)
{
this.locomotion = value;
}
}
export class Animal extends Eukaryotes
{
public isAnimal = true;
public constructor()
{
super();
}
}
export class Mammal extends Animal
{
private readonly numberOfTits: number;
public constructor(numberOfTits: number)
{
super();
this.numberOfTits = numberOfTits;
}
public get NumberOfTits(): number
{
return this.numberOfTits;
}
public SayHello(): string
{
return "Hello";
}
}
export class Feline extends Mammal
{
private readonly numberOfLives: number;
public constructor(numberOfTits: number, numberOfLives: number)
{
super(numberOfTits);
this.numberOfLives = numberOfLives == null ? 9 : numberOfLives;
this.Locomotion = TypesOfLocomotion.Walk;
}
public get NumberOfLives(): number
{
return this.numberOfLives;
}
}
}
Upvotes: 0
Reputation: 21349
This is not possible to retrieve that information at runtime, and they will never be per default possible unless you store them during the compilation. The solution is therefore be in reflection using ReflectDecorators.
Here is an excellent article covering the question of retrieving compilation time metadata at the runtime. In short: you add a decorator to the interface you would like to keep the description, this one will be converted to a JSON object which one will be stored into the code itself. During the runtime, you will be able to retrieve this JSON object having all the interface data. This is now experimental (11th Feb 2016) but in a good way.
Note: The reason why it will never by per default is basically a choice of design for TS not to overload the js code with metadata (unlike Dart).
Upvotes: 11
Reputation: 12659
This is possible by using custom transformers introduced by https://github.com/Microsoft/TypeScript/pull/13940, which is available in typescript >= 2.4.1.
My npm package, ts-transformer-keys
, is a good example.
import { keys } from 'ts-transformer-keys';
interface Props {
id: string;
name: string;
age: number;
}
const keysOfProps = keys<Props>();
console.log(keysOfProps); // ['id', 'name', 'age']
Upvotes: 39
Reputation: 251172
At runtime all of the type information is erased, so the best you can do is enumerate the properties of one of the objects. This will give you back all properties, even those that were not on the specified interface.
class GenericListRenderer<T> {
constructor(private items: T[], private className?: string){
}
private getPropertyNames(): string[] {
var properties: string[] = [];
if (this.items.length > 0) {
for (var propertyName in this.items[0]) {
console.log(propertyName);
properties.push(propertyName);
}
}
return properties;
}
render(){
var propNames: string[] = this.getPropertyNames();
}
}
class Example {
constructor(public name: string) {
}
}
var example = new Example('Steve');
var a = new GenericListRenderer<Example>([example]);
a.render();
There is also Object.keys()
, which gives you back all of the properties, although it is only supported in IE9 and above.
If you can supply more of a use case for what you want to do with the properties, it may be possible to give you an alternate solution using attributes or some other mechanism.
Upvotes: 1