Customizing private methods in TypeScript

In this specific scenario, when the public method message() is included and the greet() function operates as anticipated:

class Foo {
    public greet() {
        console.log(`Hello, ${this.getMessage()}`);
    }
    getMessage(): string {
        return "I am Foo";
    }
}

class Goo extends Foo {
    getMessage(): string {
        return "I am Goo";
    }
}

However, changing getMessage() to private causes issues with the compilation of class Goo:

class Foo {
    public greet() {
        console.log(`Hello, ${this.getMessage()}`);
    }
    private getMessage(): string {
        return "I am Foo";
    }
}

class Goo extends Foo {
    private getMessage(): string {
        return "I am Goo";
    }
}

Many developers use private methods to enhance code readability by breaking down complex functions into smaller, more manageable pieces. This practice is recommended in various programming resources. However, TypeScript poses a challenge when modifications are required for subclasses that depend on these private methods.

Is there an alternative approach to address this issue without duplicating public methods in extended classes or exposing internal methods through public interfaces?

Furthermore, it raises curiosity regarding the TypeScript team's rationale behind the restriction that allows overriding of public methods but not private methods.

Answer №1

According to bryan60, you should utilize the protected modifier:

class Foo {
  public greet() {
    console.log(`Hello, ${this.getMessage()}`);
  }

  protected getMessage(): string {
    return "I am Foo";
  }
}

class Goo extends Foo {
  protected getMessage(): string {
    return "I am Goo";
  }
}

const goo = new Goo();
goo.greet(); // Hello, I am Goo

Referencing the handbook:

The protected modifier functions similarly to the private modifier, with the added ability for members declared protected to be accessed within deriving classes.

For instance:

// Property 'getMessage' is protected and only accessible within class 'Goo'
// and its subclasses.
goo.getMessage();

class Hoo extends Goo {
  public getMessage(): string {
    return "I am Hoo";
  }

  public tryToGetGoosMessage(goo: Goo): string {
    // Property 'getMessage' is protected and can only be accessed through an
    // instance of class 'Hoo'.
    return goo.getMessage();
  }

  public doOtherHoosHoo(hoo: Hoo) {
    // This is permissible as it is inside Hoo
    hoo.hoo();
  }

  protected hoo() {}
}

const hoo = new Hoo();
// Accessing getMessage publicly in Hoo is allowed
hoo.getMessage();

// Class 'Joo' erroneously extends base class 'Hoo'.
//  Property 'getMessage' is protected in type 'Joo' but public in type 'Hoo'.
class Joo extends Hoo {
  protected getMessage(): string {
    return "I am Joo";
  }
}

Playground link

Answer №2

Utilizing the protected keyword provides the appropriate solution for this scenario!

However, in cases where direct access to the parent class is not possible (for example, when it is encapsulated within a library), one alternative approach could involve overriding a private class member-function within the constructor.

It is important to note that this method is considered somewhat unethical and should be avoided if possible!

class Foo {
    public greet() {
        console.log(`Hello, ${this.getMessage()}`);
    }
    private getMessage(): string {
        return "I am Foo"
    }
}

class Goo extends Foo {
    constructor() {
        // redefining the private member function "getMessage" 🙀
        (this as any).getMessage = () => {
            return "I am Goo"; 
        };
    }
}

Answer №3

When faced with the need to override a private method without access to the parent class, there is a more type-safe and less unconventional approach than the one suggested in mojoaxel’s answer. This alternative involves using bracket notation to access the private method:

class Foo {
    public greet() {
        console.log(`Hello, ${this.getMessage()}`);
    }

    private getMessage(): string {
        return "I am Foo";
    }
}

class Goo extends Foo {
    constructor() {
        super();
        this["getMessage"] = () => {
//          ^^^^^^^^^^^^^^ this accesses the private method
            return "I am Goo"; 
        };
    }
}

If you are utilizing ESLint and have the dot-notation rule enabled, it may be beneficial to set:

"@typescript-eslint/dot-notation": ["error", {"allowPrivateClassPropertyAccess": true}]

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

An easy guide on utilizing ngSwitch for datatype selection in Angular

While working in angular2, I came across a question regarding the usage of ngSwitch to load <div> tag based on the datatype of a variable. Is it possible to achieve something like this: <div [ng-switch]="value"> <p *ng-switch-when="isObj ...

Issue reported: "Usage of variable 'someVar' before assignment" ; however, it is being properly assigned before usage

This piece of code showcases the issue: let someVar: number; const someFunc = async () => { someVar = 1; } await someFunc(); if (someVar == 1) { console.log('It is 1'); } As a result, you will encounter ...

Update the input value with the selected option from the dropdown menu in Angular

How can I dynamically set the value of an input field based on the selection from a dropdown menu in Angular using Reactive Forms? Below is my HTML code: <nb-card> <nb-card-header> Services </nb-card-header> <nb-card-body&g ...

The TypeScript error arises when an element implicitly contains an 'any' type due to the inability to use an expression of type 'any' to index a specific type

Encountering an Issue: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ foo: string; bar: string; }'.ts(7053) Within the following code snippet: const CATEGORY_COLORS ...

Upgrade to Typescript version 3.2 and exploring the Response body within lib.dom.d.ts

Just recently upgraded to Angular 7 and Typescript 3.2.2, and now one of my Jasmine spec tests is throwing an error. httpMock.expectOne({method: 'PUT'}).flush(new Response({status: 200})); The error message reads: Argument '{ status: ...

Error Message: Angular NullInjectorException - The provider for "[Object]" is not found

While developing a simple Flashcard application that performs CRUD operations using Angular, Node.js, Express, and PostgreSQL, I encountered the following error: flashcards-area.component.ts:24 ERROR NullInjectorError: R3InjectorError(AppModule)[Flashcard ...

What is the best way to create an array of strings that can include multiple elements from a set of strings?

Exploring different roles for authorization: ['admin', 'manager', 'user'] Is there a way to create a specific type, named Roles, which is essentially a string array ( string[] ) that is limited to only containing values from ...

Ensure that only numbers with a maximum of two decimal places are accepted in Angular 2 for input

On my webpage, there are several input boxes where users can enter numbers. I need to prevent them from entering more than two decimal places. Although I tried using the html 5 input Step="0.00", it didn't work as expected. I am open to any TypeScri ...

Transforming Excel data into JSON format using ReactJS

Currently, I am in the process of converting an imported Excel file to JSON within ReactJS. While attempting to achieve this task, I have encountered some challenges using the npm XLSX package to convert the Excel data into the required JSON format. Any as ...

How can I run a TypeScript function within a JavaScript file?

I am working with a typescript file named file1.ts export function Hello(str: string) { console.log(str); } In addition, I have another file called index.js { require('./some.js'); } Furthermore, there is a script defined in the pack ...

What is the process of compiling TypeScript code?

When attempting to use tsc, I encountered issues. Even when having typescript but lacking tsc, the problem persisted. What steps should I take next? https://i.sstatic.net/Djgqb.png ...

What steps can be taken to stop Internet Explorer from caching Ajax requests in Angular2 using TypeScript?

Imagine a situation where a user has 10 points. When they click a button, an ajax call is made to the server to update the user's points after they have been used. The server should send back 9 points, which is functioning correctly on all browsers ex ...

Encountering unexpected errors with Typescript while trying to implement a simple @click event in Nuxt 3 projects

Encountering an error when utilizing @click in Nuxt3 with Typescript Issue: Type '($event: any) => void' is not compatible with type 'MouseEvent'.ts(2322) __VLS_types.ts(107, 56): The expected type is specified in the property ' ...

Find out whether the page was reloaded or accessed directly through a hyperlink

I need to find out if the page was accessed directly via a link. If it was, I need to perform a certain action. However, my current implementation is not working as intended, as even a page refresh triggers this action. Is there an alternative method to ch ...

Facing issue with local redis session not functioning as intended

I'm encountering an issue with my redis session not functioning properly when testing locally. EDIT: Additionally, I realized that it's failing to save a cookie when trying to set req.session[somekey] as undefined like so: req.session.user = u ...

Troubleshooting problems with permissions when using the AWS IAM assumeRole function with session

Within my aws-cdk application, I am working on setting up a lambda function to assume a specific role and obtain credentials through aws-sts. These credentials need to include a tenant_id tag. AWS CDK Code: Role to be assumed: const pdfUserRole = new Rol ...

There are no matching overloads in React for this call

Below is an error message in the code. It seems to be related to the usage of <IHistorical[]> in useQuery, but unfortunately, I haven't found a solution for it yet. Overload 1 of 2, '(props: Props | Readonly<Props>): ReactApexChart& ...

What is the best way to format a string into a specific pattern within an Angular application

In my Angular component, I have 4 fields: customerName, startDate, and startTime. Additionally, I have a fourth field that is a textarea where the user can see the message that will be sent via email or SMS. Within my component, I have defined a string as ...

One method of extracting an object from an array using a parameter function is by utilizing the following approach

I am searching for a specific object in an array based on the user-provided ID. var laptops = [{ "name": "Firefox", "age": 30, "id": "ab" }, { "name": "Google", "age": 35, "id": "cd", "date": "00.02.1990" }, { "na ...

Navigate through each file or image within a directory using Angular

I am facing a challenge with my app where each product has a gallery containing a random number of images, each with a completely unique name. These images are located in /src/assets/images/products/:id/. Currently, I am struggling to loop through and add ...