'In typescript, how to export the type of a private class without exporting class itself

I have a module in which public method of a public class creates and returns a new instance of a private class. The requirement is that MyClassPrivateHelper must only be instantiated by MyClass.

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 {
        /**/
    }
}

With this arrangement TypeScript reports error:

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

My goal is to export just the "type" of the private class without letting an module consuming code be able instantiate it directly. e.g.

const mycls = new module.MyClass();

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

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

I have tried using typeof like so without success.

export type Helper = typeof MyClassPrivateHelper

Maybe I am not understanding how "typeof" works. My questions are:

  • Why export of type using typeof not working?
  • How do I export type without exposing the private class outside module?


Solution 1:[1]

Why export of type using typeof not working?

export type MyInterface = typeof MyClassPrivateHelper

In this example MyInterface is the type of the constructor function but you'd like to export the type of the instances this constructor can produce.

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

Like this:

export type MyInterface = InstanceType<typeof MyClassPrivateHelper>

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

Alternatively, I found that the following also works:

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

The Interface type basically copies all public properties from T, which we then use to declare a new interface.

See https://github.com/Microsoft/TypeScript/issues/471#issuecomment-381842426 for more details.

Solution 2:[2]

You should create an interface and export that:

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

and then let the Helper implement that:

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

    public someHelperMethod(arg): void {
        this.cls.someMethod();
    }
}

The public class will return the interface

export class MyClass {
    public createHelper(): IMyHelper { 
        return new MyClassPrivateHelper(this);
    }

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

From the outside the helper is again referenced by its interface:

const helper: IMyHelper = mycls.createHelper();

Solution 3:[3]

The simplest way to do this is the following:

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

export type Helper = MyClassPrivateHelper;

Calling export type Helper = InstanceType<typeof MyClassPrivateHelper> is redundant.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2
Solution 3 Slava Fomin II