ReferenceError: Cannot access <Class> before initialization - javascript

I Have simple autoloader method that initializes and returns Instance of class using require()
Above simple require is some logic requesting info from database and checking if class exists in filesystem.
let elementClass = require('./elements/' + element.name).default;
Node and ts-node compiles without problem, but when creating instance by require i get
ReferenceError: Cannot access 'Object' before initialization
Object is main abstract class being extended by Element which is then extended by element type.
Required is last in chain class elementType
Class is initialized without problem if there is no Object class being extended, but Object class was tested without any modifications and blank constructor. And all logic transfered to Element class
Object.ts
export default class Object
{
public id: number;
public name: string;
public type: string;
public parent: number;
public owner: number;
public permission: string;
public data: {[name: string]: any};
protected dataSchema: {[name: string]: any} = {};
constructor(id: number, name: string, type: string, parent: number, owner: number, permission: string, data: {[name: string]: any})
{
if(new Validator().validate(data, this.dataSchema))
throw new Error('Invalid input data');
this.id = id;
this.name = name;
this.type = type;
this.parent = parent;
this.owner = owner;
this.permission = permission;
this.data = data;
}
}
Element.ts
import Object from "./object";
export default class Element extends Object
{
/**
* simple logic no constructor
*/
}
ElementType.ts
import Element from '../element';
export default class ElementType extends Element
{
/**
* Simple logic without constructor
*/
}
Even without any simple methods inside classes it doesn't work, it only works when Main Parent class is not used (Object)
Is there some problem requiring class that is twice extended?

Object is reserved word, I just had to rename class to something else.

Related

Typescript - can't access properties in child class via Object.keys()

I am trying to use the same logic declared in constructor of base class for all child classes.
In more details, I want to iterate over all class attributes from it's constructor.
My expectation was that if I invoke Object.keys() in base constructor and call super() in child's constructor - it will work for me.
Code example:
class BaseClass {
private baseField1: string;
private baseField2: string;
constructor(data: any) {
console.log(this.constructor.name + " has fields: " + Object.keys(this));
}
}
class ChildClass extends BaseClass {
private childField1: string;
constructor(data: any) {
super(data);
}
}
let base = new ChildClass({name: 'Joe'});
I expect to have output:
ChildClass has fields: baseField1,baseField2,childField1
But in fact I have:
ChildClass has fields: baseField1,baseField2
There is no childField1 in output
Any idea on how this is better to sort out?
UPD:
Finally I made it work as I expect. Thanks to Touffy!
class BaseClass {
private baseField1: string;
private baseField2: string;
constructor(data: any) {
Object.assign(this, data);
}
}
class ChildClass extends BaseClass {
private childField1: string;
constructor(data: any) {
super(data);
if (data.childField1) this.childField1 = data.childField1;
}
}
let base = new ChildClass({
baseField1: 'bf1',
baseFileds2: 'bf2',
childField1: 'Joe'
});
console.log('Class fields: ' + JSON.stringify(base));
Output is: Class fields: {"baseField1":"bf1","baseFileds2":"bf2","childField1":"Joe"}
The issue is not with Object.keys, but the timing of when it is called in the constructor chain. You wrote the call of Object.keys in the parent constructor, which is called by super in the child constructor before any child properties are initialized.
You need to remember that those nice property declarations are merely syntactic sugar, equivalent to this.propertyName = undefined in the constructor. Those lines are evaluated after the call to super (the parent constructor). In other words, what the JavaScript interpreter will really do is this :
class BaseClass {
constructor(data) {
this.baseField1 = undefined;
this.baseField2 = undefined
console.log(this.constructor.name + " has fields: " + Object.keys(this));
}
}
class ChildClass extends BaseClass {
constructor(data) {
BaseClass.call(this, data);
this.childField1 = undefined // too late !
}
}
If you call Object.keys in the child constructor after super(), or anywhere that's not in a constructor, it will work as expected.
However, if your goal is specifically to log all the properties of a child instance immediately when it is constructed, and have this behaviour inherited from the parent class… I don't think you can, even with Proxies.
constructor(data: any) -- this is not OOP, this is bad JavaScript habits under disguise.
Be as specific as possible with the interface of your class. Let it ask exactly what values it needs in the constructor, not just some opaque object.
class BaseClass {
public constructor(
private baseField1: string,
private baseField2: string,
) {
// Object.keys(this) is not a good idea if the class has methods
console.log(this.constructor.name + " has fields: " + Object.keys(this));
}
}
class ChildClass extends BaseClass {
public constructor(
baseField1: string,
baseField2: string,
private childField1: string;
){
super(baseField1, baseField2);
}
}
// The compiler does not allow you to pass only one argument to the constructor
// It ensures the object integrity
let base = new ChildClass('Joe');

Is it possible to access class prototype's property?

I got a question but i don't know have the right words to ask it in just one phrase.
I'm writing my code in TypeScript and I want to achieve the following:
I got an abstract "Resource" class
// Resource.ts
abstract class Resource {
public abstract readonly type: string;
protected _id?: string;
get id() {
return this._id;
}
public static info() {
return {
type: this.prototype.type
};
}
}
and, let's say, a Post resource class that inherits from the abstract class
class PostResource extends Resource {
public readonly type = 'post'
}
I want to access the "type" property from the PostResource class prototype like I tried to do with the Resource.info() static method. Obviously, it returns undefined.
I also tried to instanciate the class inside the static method like that
public static info() {
return {
type: (new this()).type
}
}
but it throws an Error because I can't instanciate Resource as it is an abstract class.
I atempted to use static properties as well :
abstract class Resource {
public static readonly type: string;
public static info() {
return {
type: this.type
}
}
}
class PostResource extends Resource {
public static readonly type = 'post';
}
That theoricaly works, but then I loose all the benefits of inheritance because static propeties and methods cannot be abstract. For example, I could create a PostResource with no type static property and TypeScript would not warn me. type would then be undefined, which is not good because all Resource-extended class should have a type property.
So I'm looking for a solution to allow me to access all properties initialized in a Resource-extended class.
I hope my question is clear, thank you for your help !
EDIT:
I should give more details about my final goal.
I would like to have a list of classes that inherit Resource abstract class, and therefore have a "type" property. Ideally it should be a static property, cause I could simply iterate over the classes and just access the property.
But I also need this value inside the instances of this class, so for example i could have something like that:
console.log(PostResource.type); // > 'post'
const post1 = new PostResource();
console.log(post1.type); // > 'post';
What about having a property _type in the abstract class and initializing it in constructor of subclasses?
abstract class Resource {
protected _type: string;
constructor(type: string) {
this._type = type;
}
public info() {
return {
type: this._type
}
}
}
class PostResource extends Resource {
constructor() {
super('post');
}
}
class GetResource extends Resource {
constructor() {
super('get');
}
}
let postResource = new PostResource();
let getResource = new GetResource();
console.log(postResource.info()); // {type: 'post'}
console.log(getResource.info()); // {type: 'get'}
Edit
I'm not sure what you're trying to achieve, but here's an example for your updated requirements:
abstract class Resource {
}
class PostResource extends Resource {
static type = 'post';
public type = 'post';
}
let postResource = new PostResource();
console.log(PostResource.type); // post
const post1 = new PostResource();
console.log(post1.type); // post
Actually your attempt with instantiating the class inside the static method can work, you just need a proper typing for this:
public static info(this: new () => Resource) {
return {
type: new this().type
}
}
Playground
More info on function's this parameter annotation.

Why instanceof returns false for a child object in Javascript

I have the Child class that extends Parent class.
So let say I created a new instance "child" out of the Child class.
When I check the condition child instanceof Child, it returns false.
However, child instanceof Parent returns true.
Why does this so?
EDIT
So I found this only happens when I extend the Child class with Error class.
Let me leave the code example below.
class Child extends Error {
constructor(message) {
super(message);
}
}
const ch = new Child();
console.log(ch instanceof Child);
2nd EDIT
class PullCreditError extends Error {
public name: string;
public message: string;
public attemptsRemaining: number;
constructor(message: string, attemptsRemaining: number) {
super();
Error.captureStackTrace(this, PullCreditError);
this.name = 'PullCreditError';
this.message = message;
this.attemptsRemaining = attemptsRemaining;
}
}
This is a documented bug:
https://github.com/Microsoft/TypeScript/issues/15875
Extending built-ins like Error, Array, and Map may no longer work
As part of substituting the value of this with the value returned by a super(...) call, subclassing Error, Array, and others may no longer work as expected. This is due to the fact that constructor functions for Error, Array, and the like use ECMAScript 6's new.target to adjust the prototype chain; however, there is no way to ensure a value for new.target when invoking a constructor in ECMAScript 5. Other downlevel compilers generally have the same limitation by default.
The recommendation is to adjust the prototype manually using setPrototypeOf in the constructor. A fix for your PullCreditError class would look like this:
export class PullCreditError extends Error {
public name: string;
public message: string;
public attemptsRemaining: number;
constructor(message: string, attemptsRemaining: number) {
super();
Object.setPrototypeOf(this, PullCreditError.prototype); // <-------
Error.captureStackTrace(this, PullCreditError);
this.name = 'PullCreditError';
this.message = message;
this.attemptsRemaining = attemptsRemaining;
}
}

Typescript implementing interface property

I have declared interface as below
interface Base {
required: string;
}
I have implemented interface in class like
class MyClass implements Base{
method(): void {
console.log(this.required);
}
}
But I am getting following error
severity: 'Error' message: 'Class 'MyClass' incorrectly implements
interface 'Base'. Property 'required' is missing in type 'MyClass'.'
at: '5,7' source: 'ts'
severity: 'Error' message: 'Property 'required' does not exist on type
'MyClass'.' at: '7,26' source: 'ts'
if I declare required: string; once again in class then no error
interface Base {
required: string;
}
class MyClass implements Base{
required: string;
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
var ss=new MyClass();
ss.method();
If you don't want to delcare requried: string twice use class instate interface for Base and extends instate implements.
class Base {
required: string;
}
class MyClass extends Base{
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
Check it out in the playground.
That's how interfaces work. If you define a property in the interface then you need to define the same property in the class where you are implementing the interface. If you would like to use required property without re define the property, you should create a class an extend it.
Your error is correct. If your class implements an interface, it must also implement all the required properties and methods. If you want not to implement some properties or methods, you can declare them as optional with ? symbol.
interface Base {
required: string;
someProperty?: string; // << look at the symbol `?` in the end of the property
}
Here you can implement the interface and left the someProperty
class MyClass implements Base{
required: string;
// someProperty is missing here, because it is optional
method(): void {
this.required="ddd";
console.log(this.required);
// you can access HTMLElement
}
}
And not only you can implement interfaces. Also you can use them as a type. If you have an interface
interface Base {
required: string;
}
you can create an object which is the type of that interface
const obj: Base = { };
But here you will get an error because if your object is of type Base, you need to provide all required properties. So you need to write
const obj: Base = { required: 'Yes' };
This will protect your code from logical errors and your code will be strong typed also for object, for which you don't want to create a class, but you want to said what shape it must be.
Example
You have an interface
interface Name {
name: string
}
and have classes
class Car implements Name {
name: string;
engine: string
constructor(name: string, engine: string){
this.name = name;
this.engine = engine;
}
}
class Person implements Name {
name: string;
surname: string;
constructor(name: string, surname: string){
this.name = name;
this.surname = surname;
}
}
var arr: Name = [new Car('Car', 'JZ'), new Person('Name', 'Surname')];
here arr is an array of type Name. So if you get arr[0], and call on it .engine, intelisense will throw an error that there is no engine property in type Name. But you can be sure, that every object in that array has name property, because the type of that array is Name and it has an required property name.

Difference between the static and instance sides of classes

I am trying to understand interface topic in Typescript
when I came across Class type, I got this code from official docs
interface ClockConstructor {
new (hour: number, minute: number);
}
class Clock implements ClockConstructor {
currentTime: Date;
constructor(h: number, m: number) { }
}
I can understand that Clock has no match for the signature new (hour: number, minute: number); that's why we get an error there.
But in docs the explaination is something which I am unable to understand. It goes in this way :
This is because when a class implements an interface, only the instance side of the class is checked. Since the constructor sits in the static side, it is not included in this check.
Any explanation would be appreciated.
The interface declares the method/members that the instances have, and not what the implementing class has.
For example check the Array and ArrayConstructor declarations:
interface Array<T> {
length: number;
toString(): string;
toLocaleString(): string;
push(...items: T[]): number;
pop(): T | undefined;
...
[n: number]: T;
}
interface ArrayConstructor {
new (arrayLength?: number): any[];
new <T>(arrayLength: number): T[];
new <T>(...items: T[]): T[];
(arrayLength?: number): any[];
<T>(arrayLength: number): T[];
<T>(...items: T[]): T[];
isArray(arg: any): arg is Array<any>;
readonly prototype: Array<any>;
}
As you can see, the Array has method/members which exist on any instance of array:
let a = [];
a.push(1, 2, 3);
console.log(a.length);
But the ArrayConstructor has the members/methods which exist on the Array itself:
console.log(Array. prototype);
console.log(Array.isArray(9));
The constructors are part of the "static" part which is why they are declared in the ArrayConstructor.
If you declare a constructor on an interface for example you'll have a problem implementing that interface:
interface MyInterface {
constructor();
getName(): string;
}
class MyClass implements MyInterface {
constructor() {}
getName() { return "name" };
}
Error:
Class 'MyClass' incorrectly implements interface 'MyInterface'. Types
of property 'constructor' are incompatible. Type 'Function' is not
assignable to type '() => void'. Type 'Function' provides no match for
the signature '(): any'.
Before you can get an instance you need to use the static side, the constructor, to get an instance. You don't need new in your interface anyway, your class is typed itself so typescript knows whatever arguments it has to pass along the constructor.
You can make benefits of an interface with type new if you want to pass a function or class that has to meet certain constructor requirements before it can be instantiated.
interface IFoo {
new(title: string);
}
function MyFunction(ctor: IFoo, title:string) {
return new ctor(title);
}
class MyClass {
constructor(public title: string) {}
}
class MySecondClass {
constructor(public title: string) {}
}
var myClass = MyFunction(MyClass, 'title');
var mySecondClass = MyFunction(MySecondClass, 'title');
console.log(myClass.title, mySecondClass.title);
In fact, a TypeScript class is a regular function in JavaScript which is static when you don't use new in front of it. This is were the docs are referring to.
// static side
function Person() {
}
Person.SayHi = function () {
return 'Hello';
}
console.log(Person.SayHi()); // static function..
var person = new Person() // instance side
See also this answer

Categories

Resources