Can we get the attributes of a javascript class? - javascript

I have this javascript class :
class UserDTO {
constructor(props) {
this.username = props.username;
this.birthday = props.birthday;
}
}
and I have a class Utils that convert an Entity to DTO:
class Utils {
convertEntityToDTO (entityObj, DTOClass) {
// entityObj is an instance of a Entity,
// DTOClass is a class not an instance
let objDTO = new DTOClass();
Object.getOwnPropertyNames(entityObj)
.filter(prop => DTOClass.hasOwnProperty(prop))
.forEach(prop => {
objDTO[prop] = entityObj[prop];
});
}
}
this doesn't work a class ; hasOwnProperty just work with object; is a way to verify if a property is an attribute of a class or not ? or I have to create an instance to test ?

You can use hasOwnProperty on the instance and getOwnPropertyNames :
class A {
constructor() {
this.ex = 'TEST';
}
}
var a = new A();
console.log(a.hasOwnProperty('ex'));
console.log(Object.getOwnPropertyNames(a));
If instead you want the methods, you need to get the prototype:
class B {
constructor() {}
exMethod() {
console.log('test');
}
}
var b = new B();
console.log(Object.getPrototypeOf(b).hasOwnProperty('exMethod'));
console.log(Object.getOwnPropertyNames(Object.getPrototypeOf(b)));

Related

Field initialization in a sub-class happens after constructor finishes in the parent class, in javascript. Is this by design?

It is easier to show than to describe it. Here is the code
let ns = {};
ns.A = class {
constructor() {
this.Virtual();
}
Virtual() {
}
};
ns.B = class extends ns.A {
constructor() {
super();
alert(this.Field);
}
Field = 0;
Virtual() {
this.Field = 123;
}
}
The alert() says that this.Field equals 0. That is, field initialization in the B class is performed after the A constructor finishes. Is this "by design" in Javascript?
If I put the Field in the prototype of the B class, then everything works OK, just like in any other language. For example
let ns = {};
ns.A = class {
constructor() {
this.Virtual();
}
Virtual() {
}
};
ns.B = class extends ns.A {
constructor() {
super();
alert(this.Field);
}
//Field = 0;
Virtual() {
this.Field = 123;
}
}
ns.B.prototype.Field;
Sorry to bother you here with this, but I don't know where is the right place to report this issue.
From https://github.com/tc39/proposal-class-fields#execution-of-initializer-expressions:
When field initializers are evaluated...
Base class: At the beginning of the constructor execution...
Derived class: Right after super() returns...
Illustration:
class A {
constructor() {
console.log('A constructor start');
this.Virtual();
console.log('A constructor end');
}
Field = (() => { console.log('A field init'); return 1})()
Virtual() {
}
};
class B extends A {
constructor() {
console.log('B constructor start')
super();
console.log('B constructor end')
}
Field = (() => { console.log('B field init'); return 2})()
Virtual() {
console.log('B virtual')
this.Field = 123;
}
};
console.log(new B())
That is, in your code, Field = 0 happens after this.Field = 123, thus overwriting it. The order of declarations doesn't matter.
If you have issues with this behaviour and wish to discuss it, https://github.com/tc39/proposal-class-fields/issues would be the right place.

use plainToClass in constructor

I have a constructor that assigns properties to the instance:
class BaseModel {
constructor (args = {}) {
for (let key in args) {
this[key] = args[key]
}
}
}
class User extends BaseModel {
name: string
}
Then I can create an instance like this:
let user = new User({name: 'Jon'})
I would now like to replace this basic functionality with class-transformer:
let user = plainToClass(User, {name: 'Jon'})
My codebase uses the first approach in many places and I would therefore like to implement the new way in the constructor so I don't break the old code:
constructor (args = {}) {
let instance = plainToClass(CLASSTYPE, args)
for (let key in Object.keys(instance)) {
this[key] = instance [key]
}
}
How can I get the class type in the constructor? I cannot use User because this is the base model and other classes may also extend from it.
I suppose it's new.target. Doc
To get constructor use new.target.prototype.constructor, and it's property name - to get class name.
class BaseModel {
typeName: string;
constructor (args = {}) {
this.typeName = new.taget.constructor.name;
}
}
class User extends BaseModel {
}
const user = new User();
const typeName = user.typeName; // this would return 'User'

JS immutable objects keep the same object type

Let's say I have a class.
class Test {
constructor() {
this.name = 'name';
this.arr = [];
}
}
And I need to create multiple instances from this class.
const entities = {
1: new Test(),
2: new Test()
}
Now, I need to update one of the properties in a shallow clone manner.
const newEntities = {
...entities,
[1]: {
...entities[1],
name: 'changed'
}
}
console.log(newEntities[1].arr === entities[1].arr) <=== true
That's works, but the problem is that [1] is a simple object and not instance of Test anymore.
How can I fix that?
You can't keep instances using object destructuring so you'll need implement this behaviour.
First example, set the new properties in the constructor:
class Test {
constructor(props) {
props = props == null ? {} : props;
this.name = 'name';
this.arr = [];
Object.assign(this, props);
}
}
const newEntities = {
...entities,
[1]: new Test({ ...entities[1], name: 'changed' })
}
Sencod example, use a custom method:
class Test {
constructor() {
this.name = 'name';
this.arr = [];
}
assign(props) {
props = props == null ? {} : props;
const instance = new Test();
Object.assign(instance, this, props);
return instance;
}
}
const newEntities = {
...entities,
[1]: entities[1].assign({ name: 'changed' })
}
You can use Object.setPrototypeOf on your [1] object.
As result, it will be:
Object.setPrototypeOf(newEntities[1], Test.prototype);

es6 class pass this to a static class functions

i have this class
import { ObjectUtilities } from '~/utils/';
class Object{
constructor(object) {
Object.assign(this, { ...object });
}
utils = ObjectUtilities;
}
and this class with the statis method also (class contains many static methods)
class ObjectUtilities {
static getKey(object){
return object.key;
}
}
and i want to know if its possible to share the "this" from the Object class
to the static method "getKey(object)"
want to do it as:
let x = new Object(object);
x.utils.getkey(this);
(ObjectUtilities as many static funcs i dont want to do it for each of them)
thanks for helping me out...
You can add a constructor to the ObjectUtilities class where you bind the given context to the getKey function:
class ObjectUtilities {
constructor(_this) {
this.getKey = this.getKey.bind(_this);
}
getKey() {
return this.key;
}
}
class MyObject {
constructor(object) {
Object.assign(this, { ...object });
this.utils = new ObjectUtilities(this);
}
}
const objectFoo = { key: 'foo' };
const objectBar = { key: 'bar' };
let x = new MyObject(objectFoo);
let y = new MyObject(objectBar);
console.log(x.utils.getKey(), y.utils.getKey());

Can you create nested classes in TypeScript?

Is there a way to nest classes in TypeScript. E.g. I'd like to use them like:
var foo = new Foo();
var bar = new Foo.Bar();
In modern TypeScript we have class expressions which you can use to create a nested class. For example you can do the following :
class Foo {
static Bar = class {
}
}
// works!
var foo = new Foo();
var bar = new Foo.Bar();
Here is a more complex use case using class expressions.
It allows the inner class to access the private members of the outer class.
class classX {
private y: number = 0;
public getY(): number { return this.y; }
public utilities = new class {
constructor(public superThis: classX) {
}
public testSetOuterPrivate(target: number) {
this.superThis.y = target;
}
}(this);
}
const x1: classX = new classX();
alert(x1.getY());
x1.utilities.testSetOuterPrivate(4);
alert(x1.getY());
codepen
I couldn't get this to work with exported classes without receiving a compile error, instead I used namespaces:
namespace MyNamespace {
export class Foo { }
}
namespace MyNamespace.Foo {
export class Bar { }
}
If you're in the context of a type declaration file, you can do this by mixing classes and namespaces:
// foo.d.ts
declare class Foo {
constructor();
fooMethod(): any;
}
declare namespace Foo {
class Bar {
constructor();
barMethod(): any;
}
}
// ...elsewhere
const foo = new Foo();
const bar = new Foo.Bar();
This answer is about a seemless nested class implementation in TypeScript which builds on top of #basarat 's answer.
To make the type of the static nested class Bar accessible (as #PeterMoore pointed out), declare the type of the nested class in a namespace. That way, we can use the shortcut Foo.Bar. By moving type typeof Foo.Bar.prototype into a type in a declared namespace, we do not have to repeat the expression.
class Foo {
static Bar = class {
}
}
declare namespace Foo {
type Bar = typeof Foo.Bar.prototype
}
// Now we are able to use `Foo.Bar` as a type
let bar: Foo.Bar = new Foo.Bar()
For static classes, the following implementation might be more elegant. This, however does not work with non-static classes.
class Foo { }
namespace Foo {
export class Bar { }
}
let bar: Foo.Bar = new Foo.Bar()
To export the class, an export statement can be added after the class and the namespace have been declared, e.g. export default Foo or export { Foo }.
To achieve the same with a non-static nested class, see the following example.
class Foo {
Bar = class {
}
}
declare namespace Foo.prototype {
type Bar = typeof Foo.prototype.Bar.prototype
}
let foo: Foo = new Foo()
let bar: Foo.prototype.Bar = new foo.Bar()
I Hope this can be helpful
Able to:
Create a new inner class instance
Access outer class instance/prototype members
Implement interfaces
Use decorators
Use Case
export interface Constructor<T> {
new(...args: any[]): T;
}
export interface Testable {
test(): void;
}
export function LogClassName<T>() {
return function (target: Constructor<T>) {
console.log(target.name);
}
}
class OuterClass {
private _prop1: string;
constructor(prop1: string) {
this._prop1 = prop1;
}
private method1(): string {
return 'private outer method 1';
}
public InnerClass = (
() => {
const $outer = this;
#LogClassName()
class InnerClass implements Testable {
private readonly _$outer: typeof $outer;
constructor(public innerProp1: string) {
this._$outer = $outer;
}
public test(): void {
console.log('test()');
}
public outerPrivateProp1(): string {
return this._$outer._prop1;
}
public outerPrivateMethod1(): string {
return this._$outer.method1();
}
}
return InnerClass;
}
)();
}
const outer = new OuterClass('outer prop 1')
const inner = new outer.InnerClass('inner prop 1');
console.log(inner instanceof outer.InnerClass); // true
console.log(inner.innerProp1); // inner prop 1
console.log(inner.outerPrivateProp1()); // outer prop 1
console.log(inner.outerPrivateMethod1()); // private outer method 1

Categories

Resources