What are the reasons behind the restriction on using non-public members in TypeScript classes?

Consider the following scenario:

class Trait {
    publicMethod() {
        this.privateMethod();
        // do something more
    }

    private privateMethod() {
        // perform a useful action
    }
}

When attempting to implement it like this:

class MyClass implements Trait {
    publicMethod() {}
}

An error is thrown stating:

MyClass improperly implements interface Trait. Property 'privateMethod' is missing in type 'MyClass'

If implemented like this:

class MyClass implements Trait {
    publicMethod() {}

    private privateMethod() {}
}

The error received is:

MyClass improperly implements interface Trait. Types have separate declarations of a private property 'privateMethod'

Attempting the following approach:

class MyClass implements Trait {
    publicMethod() {}

    public privateMethod() {}
}

An error is triggered:

MyClass improperly implements interface Trait. Property 'privateMethod' is private in type 'Trait' but not in type 'MyClass'

This limitation also applies to protected methods, private, and protected properties. It appears that all members of a class must be public in order to implement it successfully.

Why does TypeScript prohibit implementing a class with non-public members?

EDIT: The issue arises because when using `implements`, classes are treated as interfaces and interfaces cannot contain private members. One solution could be to simply ignore non-public members.

This question emerged from a desire to utilize mixins for code reuse. Although composition is an option, there exists a workaround using mixins and non-public members.

Here is a workaround solution:

function applyMixins(derivedCtor: any, baseCtors: any[]) {
    baseCtors.forEach(baseCtor => {
        Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
            derivedCtor.prototype[name] = baseCtor.prototype[name];
        })
    });
}

interface ITrait {
    publicMethod();
}

class Trait implements ITrait {
    publicMethod() {
        this.privateMethod();
        // do something more
    }

    private privateMethod() {
        // perform a useful action
    }
}

class MyClass implements ITrait {
    publicMethod() {}
}

applyMixins(MyClass, [Trait]);

Answer №1

When using the keyword implements, the Trait class is treated as an interface, but interfaces cannot have private methods.

To understand more about mixins and why private methods are not supported, check out this link: https://github.com/Microsoft/TypeScript/issues/5070

Edit: A major issue with private methods in mixins is the potential conflict when two traits have private methods with the same name. It becomes difficult to ensure that they do not interfere with each other, especially with notation like instance['my' + 'method']().

Referencing the TypeScript documentation on CodePlex:

Instead of using 'extends', we use 'implements' which treats classes as interfaces, utilizing only the types from Disposable and Activatable without the actual implementation. This means we need to provide the implementation in the class itself, contradicting the purpose of using mixins.

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

After integrating session store into my application, nestjs-sequelize is not synchronizing with any models

I'm currently working on developing a Discord bot along with a website dashboard to complement it. Everything is running smoothly, except for the backend Nestjs API that I am in the process of creating. I decided to use Sequelize as the database for m ...

Typescript filtering function that results in an empty array

Struggling with filtering an array in typescript and aurelia as I keep getting empty lists. For example, when searching for the keyword ra within the firstName property, I expect to retrieve the object with the name "Raja". Not sure where I'm going w ...

Unveiling the secrets of the Google Region Lookup API

I am struggling to incorporate the Region Area Lookup feature from Google Maps into my project. Despite it being an experimental feature, I am having difficulty getting it to function correctly. Initially, I attempted to integrate this feature into a Reac ...

Exploring the concept of asynchronous subscriptions in Angular

My current issue seems to be related to asynchronous programming, specifically with the subscription not running at the desired time. I typically approach this problem from both the user's and developer's perspectives. User's Perspective: ...

`Firebase User Instance and Custom Firestore Document`

Recently, I posted a question regarding Google Firebase Angular Firestore switchMap and encountered some issues. The question can be found here. After exploring AngularFireAuth, I learned that it is used to create a User object with fixed values, requirin ...

When imported, Node / JS instantly generates a new instance

Is there a way to instantiate a class without importing it first and using new afterward? Instead of var mainClass = require('../dist/main'); // has "class Main { ... }" var mainInstance = new mainClass(); I am looking for something like var ...

What is the best way to add a clickable link/button in Angular 8 that opens a webpage in a new tab?

I'm looking to create a website that opens in a new tab when a link or button is clicked. I'm unsure how to achieve this in Angular 8. ...

Issue encountered with Angular 13 and Twilio Sync: The variable name is not recognized as a constructor, resulting in

After upgrading to angular 13, I encountered a problem that was not present when using angular 10. In my TwilioSyncService, the require statement is included in the constructor because it is an Injectable service requirement. The code snippet shows how t ...

Angular application parametrization

My application consists of an Angular front-end, an app layer, and a DB layer. The architecture can be seen in this image. To serve the JS front-end bits to the client and proxy requests from the client to the app layer, I am using an nginx instance. If I ...

An effective method to utilize .map and .reduce for object manipulation resulting in a newly modified map

Here's an example of what the object looks like: informations = { addresses: { 0: {phone: 0}, 1: {phone: 1}, 2: {phone: 2}, 3: {phone: 3}, 4: {phone: 4}, 5: {phone: 5}, }, names: { 0 ...

I encountered an eslint error when I was trying to configure Vue 3 + Quasar with a Firebase config.ts file. The error stated that there was an unsafe assignment of an `

Recently, I set up a new Vue 3 project with Quasar using the Quasar CLI. In order to store my firebase configuration, I created a new file called src/firebase/config.ts, which looks like this: // Import necessary functions from SDKs import { initializeApp ...

The DefaultTheme in MaterialUI no longer recognizes the 'palette' property after transitioning from v4 to v5, causing it to stop functioning correctly

Currently in the process of transitioning my app from Material UI v4 to v5 and encountering a few challenges. One issue I'm facing is that the 'palette' property is not recognized by DefaultTheme from Material UI when used in makeStyles. Thi ...

The directive for angular digits only may still permit certain characters to be entered

During my exploration of implementing a digits-only directive, I came across a solution similar to my own on the internet: import { Directive, ElementRef, HostListener } from '@angular/core'; @Directive({ selector: '[appOnlyDigits]' ...

Ionic - What is the correct way to import ViewController? - Uncaught (in promise): Error: ViewController provider not found

I have a Popover in my app and I want it to behave differently based on the selected item. I followed the instructions in the Ionic documentation to achieve this. Error: Uncaught (in promise): Error: No provider for ViewController! When I tried adding ...

React Native Material - Implementing a loading indicator upon button press

With React Native Material, I am trying to implement a loading feature when a button is clicked. The goal is to show the "loading" message only when the button is active, and hide it otherwise. Additionally, I would like for the loading message to disappea ...

Callback for dispatching a union type

I am currently in the process of developing a versatile function that will be used for creating callback actions. However, I am facing some uncertainty on how to handle union types in this particular scenario. The function is designed to take a type as inp ...

Error encountered in Angular NGRX while accessing the store: Trying to read property 'map' of an undefined variable

I have integrated NGRX effects into my Angular application and encountered the following error. I'm uncertain if I am using the selector correctly in my component to query the store? core.js:6162 ERROR TypeError: Cannot read property 'map' o ...

Passing variables from a template to TypeScript using ngFor

Need help passing a variable from a template to typescript using *ngFor loop. Currently, my code looks like this: <select (change)="onregionchange()" data-placeholder="Regions" class="form-control regions-select" id="regions" multiple> <opt ...

Ways to retrieve a list of identifiers from arrays at both initial and subsequent levels

I'm currently dealing with a JSON/JavaScript structure that looks like this: { "comments": [ { "id": 1, "content": "lorem ipsum", "answers": [] }, { "id" ...

Tips for fixing typing problems (Document undefined) while including ES2017 library in the node_modules directory

When working on a legacy TypeScript project running [email protected], I encountered the need to access methods from ES2017, such as the array.includes method. To enable this, I made changes to my tsconfig.json file. Initially, it looked like this: ...