Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

child class get undefined value when calling parent function

I’m writing a playwright test and figure out that my application have a lot of similar page structures. So, I try to create an abstract class call BaseApplicationPage with the purpose to reuse them for pages in my application.

The abstract class look like this

export default abstract class BaseApplicationPage {
    page: Page;
    abstract path: string;
    abstract pageTitleText: string;

    protected constructor(page: Page) {
        this.page = page;
    }

    async navigate(): Promise<void> {
        await this.page.goto(`${process.env.TEST_URL}/${this.path}`);
        await expect(this.page).toHaveURL(new RegExp(this.path));
    }
}

and my child class look like this

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

export default class PageExample extends BaseApplicationPage{
    page: Page;
    pageTitleText: string = "example";
    path: string = "example-value";

    constructor(page: Page) {
      super(page);
    }

}

However, when I run test for the application, page becomes undefined

test.describe.serial(`Test example`, async () => {
  let page: Page;

  test.beforeAll(async ({ browser }) => {
    page = await setUpPage(browser);
  });

  test("Test view page @healthcheck", async () => {
    const pageExample = new PageExample(page);
    await pageExample.navigate();
  });
});
 TypeError: Cannot read properties of undefined (reading 'goto')

       at ../pages/base-application-page.ts:14

      12 |
      13 |     navigate = async (): Promise<void> => {
    > 14 |         await this.page.goto(`${process.env.TEST_URL}/${this.path}`);
         |                         ^

I’ve made sure that page object isn’t undefined before calling new PageExample(page);. However, it suddenly become undefined when it’s inside the constructor. Why is that?

>Solution :

Your child class redefines page. It shouldn’t. Instead, it should just inherit it. Remove page: Page; from your child class:

export default class PageExample extends BaseApplicationPage{
    // *** no `page: Page;` here
    pageTitleText: string = "example";
    path: string = "example-value";

    constructor(page: Page) {
      super(page);
    }

}

Your code may have worked in earlier versions of TypeScript or earlier versions of your project, but when public class fields were standardized in JavaScript, they were standardized slightly differently from the way TypeScript implemented them, and so TypeScript now has an option for how to handle this: useDefineForClassFields, which defaults to true if target is "ES2022" or higher (including "ESNext").

With TypeScript’s old semantics, the page property would have been created via simple assignment in the parent constructor and not reassigned by the child. But with JavaScript’s standard semantics, the property gets redefined (as though the child class had Object.defineProperty(this, "page", { value: undefined, /*...flags...*/ });.

As this is a JavaScript thing, you can see it in pure JavaScript as well:

class Parent {
    value;
    constructor(v) {
        this.value = v;
    }
}
class BadChild extends Parent {
    value;
    constructor(v) {
        super(v);
    }
}
class GoodChild extends Parent {
    // No redeclaration
    constructor(v) {
        super(v);
    }
}
const p = new Parent(42);
console.log(p.value); // 42
const b = new BadChild(42);
console.log(b.value); // undefined
const g = new GoodChild(42);
console.log(g.value); // 42

Notice how BadChild redefines value and so resets the property to undefined, but GoodChild inherits it.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading