Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript - retrieving class attributes without creating object

In angular 6, I have a typescript class like this

export class Hello {
  id: number;
  name?:string;
  txt?:string;
}

From a component controller, I would like to retrieve an array with the property names, without needing to create an object. Something like:

ObjectType.keys(Hello);
// ["id", "name", "txt"]

Is that possible ?

like image 636
Robycool Avatar asked Jul 04 '18 09:07

Robycool


2 Answers

i believe this is not possible but what is possible if you declare the property static and then access with direct reference of class like

 class Hello{
        static id: number;
        static  name?:string;
        static  txt?:string;
    }
    Hello.id;

obviously the other way is creating object by putting constructor in it and initialization with default value,

like image 50
Irfan Nasim Avatar answered Sep 22 '22 21:09

Irfan Nasim


If the fields are not initialized even creating an object will not retrieve all members of the class. If a field is just declared, the compiler will know about it and offer type checking but it will not emit any code for it, so if you use Object.keys only field that were assigned a value will be retrieved.

One option is to just create an object with all the same fields as the original (minus methods) and get the keys if this object. We can get the compiler to check that this object will have exactly the same fields as the class, no more and no less, so if we change the class we will get an error if we forget to change this object as well.

export class Hello {
    id: number;
    name?: string;
    txt?: string;
    method() { }
}

type NonMethodKeys<T> = { [P in keyof T]: T[P] extends Function ? never : P }[keyof T];
function getFields<T>(fields: { [P in NonMethodKeys<T>]: true }) {
    return Object.keys(fields)
}

getFields<Hello>({
    id: true,
    name: true,
    txt: true // if we comment this out we would get an error
    //noAProp : true;  //this would be an error
});

We can even extend this approach to creating an object that also has type information about the properties, again forcing the compiler to check that the object is accurate. For primitive types, we will pass a string representing the type name, for object properties we can either pass in the class constructor or just object:

interface IProp { }

export class Hello {
    id: number;
    name?: string;
    txt?: string;
    subHi: Hello;
    prop: IProp;
    method() { }
}

type NonMethodKeys<T> = { [P in keyof T]: T[P] extends Function ? never : P }[keyof T];
type TypeOf<T> = T extends boolean ? 'boolean' :
    T extends string ? 'string' :
    T extends number ? 'number' :
    'object' | (new (...args: any[]) => T);

function getFields<T>(fields: { [P in NonMethodKeys<T>]: TypeOf<T[P]> }) : typeof fields{
    return fields
}

let fields = getFields<Hello>({
    id: 'number',
    name: 'string',
    txt: 'string',
    subHi: Hello,
    prop: 'object'
});
like image 32
Titian Cernicova-Dragomir Avatar answered Sep 22 '22 21:09

Titian Cernicova-Dragomir