This is my classes:
export class Parent {
protected static name: string;
public getName() {
return Parent.name
}
}
export class Child1 extends Parent {
constructor() {
super()
if (!Child1.name) {
// connect to database for get names
Child1.name = '1';
}
}
}
export class Child2 extends Parent {
constructor() {
super()
if (!Child2.name) {
// connect to database for get names
Child2.name = '2';
}
}
}
I run this code:
let child1 = new Child1()
let child2 = new Child2()
console.log(child1.getName())
console.log(child2.getName())
And I get this result:
undefined
undefined
But I get this result:
1
2
I want to connect to database and get names, so per new class I dont want to connect to database again.
Parent.name will always access the name property of Parent. If you want to make it conditional on which instance the function is called on you have to use this.constructor.name instead:
public getName() {
return this.constructor.name
}
this.constructor refers to the object's constructor function / class.
class Parent {
getName() {
return this.constructor.db
// ^^^^^^^^^^^^^^^^
}
}
class Child1 extends Parent {
constructor() {
super()
if (!Child1.db) {
// connect to database for get names
Child1.db = '1';
}
}
}
class Child2 extends Parent {
constructor() {
super()
if (!Child2.db) {
// connect to database for get names
Child2.db = '2';
}
}
}
let child1 = new Child1()
let child2 = new Child2()
console.log(child1.getName())
console.log(child2.getName())
The problem is static members are bound to the class and can not be referenced via an instance.
Use it like this:
class Parent {
protected static name: string;
public getName() {
return Parent.name
}
}
class Child1 extends Parent {
constructor() {
super()
if (!Parent.name) {
Parent.name = '1';
}
}
}
class Child2 extends Parent {
constructor() {
super()
if (!Parent.name) {
// connect to database for get names
Parent.name = '2';
}
}
}
let child1 = new Child1();
let child2 = new Child2();
console.log(child1.getName());
console.log(child2.getName());
export class Parent {
protected namE: string;
public getName() {
return this.namE
}
}
export class Child1 extends Parent {
constructor() {
super()
if (!this.namE) {
// connect to database for get namEs
this.namE = '1';
}
}
}
export class Child2 extends Parent {
constructor() {
super()
if (!this.namE) {
// connect to database for get namEs
this.namE = '2';
}
}
}
let child1 = new Child1()
let child2 = new Child2()
console.log(child1.getName())
console.log(child2.getName())
OutPut:
1
2
Why don't you do it this way?
Related
i am having some issues learning class methods syntaxes... there are a few and I dont know if they have different behaviours or if they are just equivalent/updated versions of one another.
is my thoughts real?
class Person {
constructor(name) {
this.name = name
}
instanceMethod() { // this.
console.log('hello there')
}
}
Person.prototype.instanceMethod = () => { //equivalent to this?
}
const person = new Person()
person.instanceMethod // so we call the "person.prototype.instanceMethod'' in the instance of the class
// ... //
class Person {
constructor(name) {
this.name = name
}
static staticMethod() { // this.
console.log('hello there')
}
}
class OtherPerson extends Person {
}
Person.staticMethod = () => { // is equivalent to this?
}
const person = new Person()
Person.staticMethod() // so we call the static method in the Parent Class
OtherPerson.staticMethod //or a child class of that Parent Class.
or to call in the child class of the class you have to provide their own static methods?
class OtherPerson extends Person {
(... constructor and stuff...)
staticMethod() { console.log('hello there') } // defining their own static methods.
staticMethod() { super.staticMethod() } //or make this as well... both with same results
}
I want to use this.props.childName in child function that is defined in the parent function.
But it's TypeScript compile error (Property 'name' does not exist...)
If I use this.props.parentName, it's ok.
How can I access this.props of child class?
interface Prop<T> {
parentName: string
}
class Parent<T> extends React.Component<Prop<T>, State<T>> {
constructor(props: Prop<T>) {
super(props)
}
printName() {}
}
interface PropChildren<T> {
childName: string
}
class Child<T> extends Parent<string> {
constructor(props: PropChildren<T>) {
super(props)
}
printName() {
console.log(this.props.childName) // here I want to use children prop but compile error
}
}
Your child component extends the parent component, and the type of props in the parent is Prop<T>, which contains only the property parentName.
In order to have PropChildren as the type of props in the child component you should declare it as:
class Child<T> extends React.Component< PropChildren<T>, State<T>> {
// ...
}
By the way, you don't need to make your props interfaces generic (with <T>). The generics are used only when the interface can be used in different contexts with different data types.
Based on your comment, here is an example of how you can share the behavior of the parent with the child, but still being able to define a different data type for the child's props:
interface PropParent {
parentName: string
}
class Parent<TProp extends PropParent> extends React.Component<TProp, State> {
constructor(props: TProp) {
super(props)
}
printName() {}
}
interface PropChildren extends PropParent {
childName: string
}
class Child<T> extends Parent<PropChildren> {
constructor(props: PropChildren) {
super(props)
}
printName() {
console.log(this.props.childName)
}
}
first, you don't need any Generics in interface unless you need to use it in different places.
second, class Child should also extend from React.Component not from its parent.
so here is what might be a better code
import React from 'react'
interface IParentProps {
readonly parentName: string;
readonly children?: JSX.Element
}
interface IPropsChild {
readonly childName: string;
}
class Parent extends React.Component<IParentProps> {
constructor(props: IParentProps) {
super(props)
}
printName = () => {
}
render() {
return <Child childName={this.props.parentName} />
}
}
class Child extends React.Component<IPropsChild> {
constructor(props:IPropsChild) {
super(props)
}
printName = () => {
console.log(this.props.childName)
}
}
In order to allow both, proper props definition and the child class derive from the parent class you have to include the props type in your definition:
interface ParentProp<T> {
parentName: string;
}
export class Parent<T, P = ParentProp<T>, S = {}, SS = any> extends React.Component<P, S, SS> {
public printName() {
// console.log(this.props.parentName); Doesn't compile, as P can be any prop interface.
}
}
interface ChildProp<T> {
childName: string;
}
export class Child<T> extends Parent<T, ChildProp<T>> {
public printName() {
console.log(this.props.childName);
}
}
I want to write a simple function in the child class that only returns it's own keys (and not the parent).
class Parent{
protected _parentAttribute!: string;
constructor() {
this._parentAttribute='test';
}
}
class Child extends Parent{
childAttribute!: string;
constructor() {
super();
console.log("My unique child keys are:", Object.keys(this));
}
}
let child=new Child();
Result:
My unique child keys are: [_parentAttribute,childAttribute]
Desired result: My unique child keys are: [childAttribute]
Is this possible?
First, create a variable at the top class and then in that variable, store the keys which are in the top class. Use a filter function inside the child class to filter the top variables. There's nothing bad in this approach as I think. The filter should work fine and this method should work every time.
class Parent{
protected _parentAttribute: string;
protected topKeys;
constructor() {
this._parentAttribute='test';
this.topKeys = 'test' // asign something it so it comes in your property names
let somevar = Object.getOwnPropertyNames(this) // get all the properties
this.topKeys = somevar // put them in this variable
}
}
class Child extends Parent{
public childAttribute: string;
constructor() {
super();
this.childAttribute = 'test'
let keyofChild = Object.keys(this).filter(keys => !this.topKeys.includes(keys))
console.log("My unique child keys are:", keyofChild); // childAttribute
}
}
let child = new Child();
Ended up getting it like this. However, feels hacky and I'm open to better answers:
class Parent{
protected _parentAttribute: string;
protected _parentAttribute2: string;
protected _parentKeyList: Array<string>;
constructor() {
this._parentAttribute='test';
this._parentAttribute2='another value';
this._parentKeyList=['']; //Oddly necessary...
this._parentKeyList=Object.keys(this); //Must be at the end of constructor
}
class Child extends Parent{
childAttribute: string;
constructor() {
super();
const uniqueKeys=_.difference(Object.keys(this),this._parentAttributes); //Using lodash
console.log("My unique child keys are:", uniqueKeys);
}
let child=new Child();
after super() the child is equal to the parent as are its properties
TYPESCRIPT:
class Parent {
_parentAttribute1: string = "test"
_parentAttribute2 = 'test';
constructor() {
}
}
class Child extends Parent {
getParentPropertyNames(): Array<string>{
delete this.parentPropertyNames;
return Object.getOwnPropertyNames(this)
}
// this is the magic as it get called immediatly after super()
parentPropertyNames: Array<string> = this.getParentPropertyNames()
// </magic>
childAttribute1 = 'test';
childPropertyNames!: string[]
constructor() {
super();
}
get uniqueNames(){
this.childPropertyNames = Object.getOwnPropertyNames(this)
//#ts-ignore
.filter(name => !this.parentPropertyNames.includes(name)) // wastefull computation
console.log(this.childPropertyNames)
return this.childPropertyNames
}
}
let child = new Child();
child.uniqueNames
parsed for running on SO
class Parent {
constructor() {
this._parentAttribute1 = "test";
this._parentAttribute2 = 'test';
}
}
class Child extends Parent {
constructor() {
super();
// this is the magic as it get called immediatly after super()
this.parentPropertyNames = this.getParentPropertyNames();
// </magic>
this.childAttribute1 = 'test';
}
getParentPropertyNames() {
delete this.parentPropertyNames;
return Object.getOwnPropertyNames(this);
}
get uniqueNames() {
this.childPropertyNames = Object.getOwnPropertyNames(this)
//#ts-ignore
.filter(name => !this.parentPropertyNames.includes(name)); // wastefull computation
console.log(this.childPropertyNames);
return this.childPropertyNames;
}
}
let child = new Child();
child.uniqueNames;
I have a class A, and a class B inherited from it.
class A {
constructor(){
this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
When I run this code, I get the following error:
Uncaught TypeError: Cannot read property 'value' of undefined
How can I avoid this error?
It's clear for me that the JavaScript code will call the init method before it creates the myMember, but there should be some practice/pattern to make it work.
This is why in some languages (cough C#) code analysis tools flag usage of virtual members inside constructors.
In Typescript field initializations happen in the constructor, after the call to the base constructor. The fact that field initializations are written near the field is just syntactic sugar. If we look at the generated code the problem becomes clear:
function B() {
var _this = _super.call(this) || this; // base call here, field has not been set, init will be called
_this.myMember = { value: 1 }; // field init here
return _this;
}
You should consider a solution where init is either called from outside the instance, and not in the constructor:
class A {
constructor(){
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
x.init();
Or you can have an extra parameter to your constructor that specifies whether to call init and not call it in the derived class as well.
class A {
constructor()
constructor(doInit: boolean)
constructor(doInit?: boolean){
if(doInit || true)this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor()
constructor(doInit: boolean)
constructor(doInit?: boolean){
super(false);
if(doInit || true)this.init();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
Or the very very very dirty solution of setTimeout, which will defer initialization until the current frame completes. This will let the parent constructor call to complete, but there will be an interim between constructor call and when the timeout expires when the object has not been inited
class A {
constructor(){
setTimeout(()=> this.init(), 1);
}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
// x is not yet inited ! but will be soon
Because myMember property is accessed in parent constructor (init() is called during super() call), there is no way how it can be defined in child constructor without hitting a race condition.
There are several alternative approaches.
init hook
init is considered a hook that shouldn't be called in class constructor. Instead, it is called explicitly:
new B();
B.init();
Or it is called implicitly by the framework, as a part of application lifecycle.
Static property
If a property is supposed to be a constant, it can be static property.
This is the most efficient way because this is what static members are for, but the syntax may be not that attractive because it requires to use this.constructor instead of class name if static property should be properly referred in child classes:
class B extends A {
static readonly myMember = { value: 1 };
init() {
console.log((this.constructor as typeof B).myMember.value);
}
}
Property getter/setter
Property descriptor can be defined on class prototype with get/set syntax. If a property is supposed to be primitive constant, it can be just a getter:
class B extends A {
get myMember() {
return 1;
}
init() {
console.log(this.myMember);
}
}
It becomes more hacky if the property is not constant or primitive:
class B extends A {
private _myMember?: { value: number };
get myMember() {
if (!('_myMember' in this)) {
this._myMember = { value: 1 };
}
return this._myMember!;
}
set myMember(v) {
this._myMember = v;
}
init() {
console.log(this.myMember.value);
}
}
In-place initialization
A property may be initialized where it's accessed first. If this happens in init method where this can be accessed prior to B class constructor, this should happen there:
class B extends A {
private myMember?: { value: number };
init() {
this.myMember = { value: 1 };
console.log(this.myMember.value);
}
}
Asynchronous initialization
init method may become asynchronous. Initialization state should be trackable, so the class should implement some API for that, e.g. promise-based:
class A {
initialization = Promise.resolve();
constructor(){
this.init();
}
init(){}
}
class B extends A {
private myMember = {value:1};
init(){
this.initialization = this.initialization.then(() => {
console.log(this.myMember.value);
});
}
}
const x = new B();
x.initialization.then(() => {
// class is initialized
})
This approach may be considered antipattern for this particular case because initialization routine is intrinsically synchronous, but it may be suitable for asynchronous initialization routines.
Desugared class
Since ES6 classes have limitations on the use of this prior to super, child class can be desugared to a function to evade this limitation:
interface B extends A {}
interface BPrivate extends B {
myMember: { value: number };
}
interface BStatic extends A {
new(): B;
}
const B = <BStatic><Function>function B(this: BPrivate) {
this.myMember = { value: 1 };
return A.call(this);
}
B.prototype.init = function () {
console.log(this.myMember.value);
}
This is rarely a good option, because desugared class should be additionally typed in TypeScript. This also won't work with native parent classes (TypeScript es6 and esnext target).
One approach you could take is use a getter/setter for myMember and manage the default value in the getter. This would prevent the undefined problem and allow you to keep almost exactly the same structure you have. Like this:
class A {
constructor(){
this.init();
}
init(){}
}
class B extends A {
private _myMember;
constructor(){
super();
}
init(){
console.log(this.myMember.value);
}
get myMember() {
return this._myMember || { value: 1 };
}
set myMember(val) {
this._myMember = val;
}
}
const x = new B();
Try this:
class A {
constructor() {
this.init();
}
init() { }
}
class B extends A {
private myMember = { 'value': 1 };
constructor() {
super();
}
init() {
this.myMember = { 'value': 1 };
console.log(this.myMember.value);
}
}
const x = new B();
Super has to be first command. Remeber that typescript is more "javascript with documentation of types" rather than language on its own.
If you look to the transpiled code .js it is clearly visible:
class A {
constructor() {
this.init();
}
init() {
}
}
class B extends A {
constructor() {
super();
this.myMember = { value: 1 };
}
init() {
console.log(this.myMember.value);
}
}
const x = new B();
Do you have to call init in class A?
That works fine, but I don't know if you have different requirements:
class A {
constructor(){}
init(){}
}
class B extends A {
private myMember = {value:1};
constructor(){
super();
this.init();
}
init(){
console.log(this.myMember.value);
}
}
const x = new B();
More often than not, you can defer the call of init() to a time just before it is needed by hacking into one of your getters.
For example:
class FoodieParent {
public init() {
favoriteFood = "Salad";
}
public _favoriteFood: string;
public set favoriteFood(val) { this._favoriteFood = val; }
public get favoriteFood() {
if (!this._favoriteFood) {
this.init();
}
return this._favoriteFood;
}
public talkAboutFood() {
// init function automatically gets called just in time, because "favoriteFood" is a getter
console.log(`I love ${this.favoriteFood}`);
}
}
// overloading the init function works without having to call `init()` afterwards
class FoodieChild extends FoodieParent {
public init() {
this.favoriteFood = "Pizza"
}
}
Like this :
class A
{
myMember;
constructor() {
}
show() {
alert(this.myMember.value);
}
}
class B extends A {
public myMember = {value:1};
constructor() {
super();
}
}
const test = new B;
test.show();
is there a restriction on multi level inheritance with es6 classes. I am adding some additional functionality to a framework/ external class.
class B extends External.A {
// ...
}
class C extends B {
// ...
}
and use like
const result = new C(data);
gives error
TypeError: Class constructor B cannot be invoked without 'new'
But, if I use class A directly, there is no error
class C extends External.A {
// ...
}
const result = new C(data);
// works fine
Edit:
I use "babel": "^6.5.2" to transpile everything. In real code, all 3 classes lives in different file and uses module system to export and import them. if that matters.
Original answer
Bare bones of your problem works like charm.
Probably your code is lack of super() in constructor()
class A {
constructor() {
document.write('from A <br/>');
}
}
class B extends A {
constructor() {
super();
document.write('from B <br/>');
}
}
class C extends B {
constructor() {
super();
document.write('from C <br/>');
}
}
new C();
Here is fiddle you can play with: https://jsfiddle.net/nkqkthz2/
Edit
In your original question you pass some data to C class constructor new C(data);, then if you want to handle this in your chain of classes, you should write own constructor function:
class A {
constructor(data) {
document.write(`${data} A <br/>`);
}
}
class B extends A {
someFunc() {
//
}
}
class C extends B {
constructor(data) {
super(data);
this.data = data;
}
write() {
document.write(`${this.data} C <br/>`);
}
}
const c = new C('test');
c.write();
https://jsfiddle.net/rwqgm9n0/
Pay attention that class B you don't need specify constructor since default constructor is:
constructor(...args) {
super(...args);
}
and this can pass data to class A constructor.
If you omit super in class C constructor you newer pass data to class A which can produce your error.
class A {
constructor(data) {
document.write(`${data} A <br/>`);
}
}
class B extends A {
someFunc() {
//
}
}
class C extends B {
constructor(data) {
//super(data);
this.data = data;
}
write() {
document.write(`${this.data} C <br/>`);
}
}
const c = new C('test');
c.write();
https://jsfiddle.net/jwm5xjcp/
Multilevel Inheritance:
class Person{
constructor(firstName, middleName, lastName){
console.log("Person constructor........");
this.firstName=firstName;
this.middleName=middleName;
this.lastName=lastName;
}
fullName() {
return `${this.firstName}${this.middleName==null?' ':' '+this.middleName+' '}${this.lastName}`;
}
}
//let person = new Person("FirstName",null, "LastName");
//console.log('Full Name: '+person.fullName());
// INHERITANCE
class Employee extends Person{
constructor(employeeId, joinDate,firstName, middleName, lastName){
super(firstName, middleName, lastName);
console.log("Employee constructor........");
this.employeeId = employeeId;
this.joinDate = joinDate;
}
}
/*let employee = new Employee(12, '2017-02-01', "FirstName",null, "LastName");
console.log('Full Name: '+employee.fullName());
console.log('Employee ID: '+employee.employeeId);
console.log('Join Date: '+employee.joinDate);*/
class EmpOne extends Employee{
constructor(empOneId,employeeId, joinDate,firstName, middleName, lastName){
super(employeeId, joinDate,firstName, middleName, lastName);
console.log("EmpOne constructor.........");
this.empOneId = empOneId;
}
}
let newEmpOne = new EmpOne("emp one ID", 13, '2018-02-01', "FirstName",null, "LastName");
console.log("==================================")
console.log('Full Name: '+newEmpOne.fullName());
console.log('Employee ID: '+newEmpOne.employeeId);
console.log('Join Date: '+newEmpOne.joinDate);
console.log('EMP ONE: '+newEmpOne.empOneId);