How can you export the type of a private class in TypeScript without exporting the class itself?

I am facing a dilemma in my module where the public method of a public class is responsible for creating and returning a new instance of a private class. The stipulation is that only MyClass should have the capability to instantiate MyClassPrivateHelper.

class MyClassPrivateHelper {
    constructor(private cls: MyClass) {
    }

    public someHelperMethod(arg): void {
        this.cls.someMethod();
    }
}
export class MyClass {
    public createHelper(): MyClassPrivateHelper {  // error here
        return new MyClassPrivateHelper(this);
    }

    public someMethod(): void {
        /**/
    }
}

In TypeScript, there is an error reported with this setup:

[ts] Return type of public method from exported class has or is using private name 'MyClassPrivateHelper'.

The objective is to export solely the "type" of the private class without allowing external code to directly instantiate it. For example,

const mycls = new module.MyClass();

// should be allowed
const helper: MyClassPrivateHelper = mycls.createHelper();

// should not be allowed
const helper = new module.MyClassPrivateHelper();

I attempted utilizing typeof but did not find success.

export type Helper = typeof MyClassPrivateHelper

It seems like I may not fully grasp how "typeof" functions. My inquiries are:

  • Why does exporting a type using typeof not function as expected?
  • How can I export a type without exposing the private class outside the module?

Answer №1

Why isn't the export of type working with typeof?

export type MyInterface = typeof MyClassPrivateHelper

In this scenario, MyInterface represents the type of the constructor function, but what you actually want is to export the type of the instances that this constructor can create.

How can I export a type without exposing the private class outside the module?

You can achieve this by:

export type MyInterface = InstanceType<typeof MyClassPrivateHelper>

The purpose of InstanceType is briefly explained here: https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-8.html.

Alternatively, another approach that works is:

type Interface<T> = { [P in keyof T]: T[P] }
export interface MyInterface extends Interface<MyClassPrivateHelper> {}

The Interface type essentially replicates all public properties from T, which are then used to define a new interface.

For more information, refer to: https://github.com/Microsoft/TypeScript/issues/471#issuecomment-381842426.

Answer №2

To effectively manage your code, it is recommended to establish an interface and export it:

export interface IMyHelper {
   provideHelp(arg): void;
}

Subsequently, instruct the Helper to adhere to this interface:

class MyClassSecretHelper implements IMyHelper {
    constructor(private cls: MyClass) {
    }

    public provideHelp(arg): void {
        this.cls.performAction();
    }
}

The main class will offer a method to create and return the helper instance

export class MyClass {
    public generateHelper(): IMyHelper { 
        return new MyClassSecretHelper(this);
    }

    public performAction(): void {
        /**/
    }
}

Externally, the helper can be accessed through its interface:

const helper: IMyHelper = mycls.generateHelper();

Answer №3

One way to achieve this task is as follows:

class MyClassPrivateHelper {
    constructor(private cls: any) {
    }
    public someHelperMethod(arg: any): void {
        this.cls.someMethod();
    }
}

export type Helper = MyClassPrivateHelper;

The instruction

export type Helper = InstanceType<typeof MyClassPrivateHelper>
serves no additional purpose.

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

NextAuth is failing to create a session token for the Credential provider

Currently, I am in the process of developing an application using the t3 stack and am facing a challenge with implementing the credential provider from nextauth. Whenever I attempt to log a user in, I encounter an error in the console displaying the messag ...

Identify numbers and words within a sentence and store them in an array

Looking to split a string into an array based on type, extracting numbers and floats. The current code is able to extract some values but not complete. var arr = "this is a string 5.86 x10‘9/l 1.90 7.00" .match(/\d+\.\d+|\d+&bsol ...

Adjusting the date in Angular 8 by increasing or decreasing it in the dd-MM-yyyy layout with a button press

How can I dynamically adjust the date in an input box by using buttons to increment and decrement it? Below is the code snippet: prev() { let diff = 1; //1 to increment and -1 to decrement this.date.setDate(this.date.getDate() - diff ...

How to extract a JavaScript object from an array using a specific field

When dealing with an array of objects, my goal is to choose the object that has the highest value in one of its fields. I understand how to select the value itself: Math.max.apply(Math, list.map(function (o) { return o.DisplayAQI; })) ... but I am unsur ...

To run multiple environments with react-native-dotenv in a React Native project using Typescript, only the local environment is activated

Currently, I am facing an issue while trying to initialize my React Native app with TypeScript in three different environments - development, local, and testing. When I attempt to run APP_ENV=testing expo start or APP_ENV=development expo start, it always ...

Access the $event object from an Angular template selector

<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script> <input type="file" #myFile multiple /> <button (click)="onDelete(myFile.event)">DeleteFiles</button> My code snippet is experienci ...

How can I change a ReactNode into a text format?

I am looking for a way to convert the following code snippet into a string while preserving Tailwind CSS and other elements. My project uses Next.js with TypeScript and Tailwind CSS. Input : export default function Header_1() { return ( <div clas ...

Angular - Using the DatePipe for date formatting

Having trouble displaying a date correctly on Internet Explorer using Angular. The code works perfectly on Chrome, Firefox, and other browsers, but not on IE. Here is the code snippet : <span>{{menu.modifiedDate ? (menu.modifiedDate | date : "dd-MM- ...

Typescript: The original type cannot be indexed with a type-mapped type

I'm currently working on a class where I need to define a method that returns an object with keys based on the generic type inferred by the compiler. However, I've encountered an issue with the code snippet below. The compiler is indicating that ...

Learning to implement the latest language features in JavaScript on older runtimes using TypeScript

Currently, I am faced with a dilemma in my TypeScript project involving the use of flatMap. The issue arises from the fact that this project needs to be compatible with Node.js versions as old as v10, which do not support flatMap. I had assumed that TypeS ...

Struggling to retrieve posted data using Angular with asp.net

I have encountered an issue while sending a post request from Angular to my ASP.NET server. I am trying to access the values of my custom model class (SchoolModel) and I can see that all the values are correct inside Angular. However, when I attempt to ret ...

Issue with pre-selected default value in AngularJS TypeScript Kendo UI DropDownList

I have successfully implemented a list of objects for items, but now I am facing a challenge in adding a default selected value. Below is the HTML code for the drop-down: <select kendo-drop-down-list k-options="selectItems" k-ng-mode ...

What causes the select dropdown to display an empty default in Angular 8 following an HTTP request?

I have created a simple HTML code to populate array elements in a dropdown list, with the default value being fetched from an HTTP service during the ngOnInit lifecycle hook. However, I am encountering an issue where the default value is displayed as empty ...

Updating a global variable in Ionic 3

I recently started exploring the world of Ionic, Angular, and TypeScript. I encountered a scenario where I needed to have a variable "ar: AR" accessible across all pages. To achieve this, I decided to make it a global variable by following these steps: Fi ...

What is the process for implementing a new control value accessor?

I have a directive that already implements the ControlValueAccessor interface (directive's selector is input[type=date]) and now I need another directive that also implements ControlValueAccessor with the selector input[type=date][datepicker] - let&ap ...

Troubleshooting: React.forwardRef and Typescript defaultProps not functioning correctly

I am currently working on migrating components from a js to ts react component library for my own project. The library was originally written in js using a customized material-ui library. My task now is to migrate these components one by one. Here is an ...

Is there a way for me to use TypeScript to infer the type of the value returned by Map.get()?

type FuncType<O extends Object> = (option: O) => boolean export const funcMap: Map<string, Function> = new Map() const func1: FuncType<Object> = () => true const func2: FuncType<{prop: number}> = ({ prop }) => prop !== 0 ...

Arrange information in table format using Angular Material

I have successfully set up a component in Angular and Material. The data I need is accessible through the BitBucket status API: However, I am facing an issue with enabling column sorting for all 3 columns using default settings. Any help or guidance on th ...

What exactly is an npm "modular construction" and what is the process for setting it up?

I am aiming to integrate sortablejs's MultiDrag feature with Vuejs2 and Typescript. The official documentation states: MultiDrag is a plugin for SortableJS, but it may not be included in all of Sortable's builds. It comes pre-installed in the ...

Preventing memory leaks in unmounted components: A guide

Currently, I am facing an issue while fetching and inserting data using axios in my useState hook. The fetched data needs to be stored as an array, but unfortunately, I encountered a memory leak error. I have tried various solutions including using clean u ...