How to export a function from a module as a class member

How to export a function from a module as a class property?

When class has a property which points to a function inside a module, that property acts like its private regardless of any modifiers (adding public doesnt do anything here)

For example, here are 3 ways to define property (a, b, c in example) that all do the same thing, but none give me what I’d like – which is to define function in some other module so class body does not grow out of control

// myModule.ts
/** typedef this is a test */
export function func(msg) {
  console.log(msg);
}

// myClass.ts
import * as myModule from './myModule';
export class MyClass {
  a(msg) { return myModule.func(msg); }
  b = (msg) => myModule.func(msg);
  c = myModule.func;
}

// index.ts
import { myClass } from './myClass';
const classInstance = new myClass();
classInstance.a('test'); // works but this way i cannot get typedefs from a module
classInstance.b('test'); // added just for completness, but its same problem as c
classInstance.c('test'); // works but tsc complains about property 'c' does not exist on type 'MyClass'

Answer

Started digging – issue is not when working fully inside TypeScript project,
issue is when compiling a TS module with class to a JS and then consuming that JS from anywhere else

compiled by TSC with target=es2020
TSC hides properties and they are only visible if typedefs are available
If typedefs are not available for any reason, properties .b and .c are reported as does not exist on type MyClass

Basically, TSC creates JS that is valid to execute, but is otherwise not really correct

myClass.js

export class MyClass {
  constructor() {
      Object.defineProperty(this, "b", {
          enumerable: true,
          configurable: true,
          writable: true,
          value: (msg) => myModule.func(msg)
      });
      Object.defineProperty(this, "c", {
          enumerable: true,
          configurable: true,
          writable: true,
          value: myModule.func
      });
  }
  a(msg) {
      return myModule.func(msg);
  }
}

myclass.d.ts

export declare class MyClass {
    a(msg: string): void;
    b: (msg: string) => void;
    c: typeof myModule.func;
}

When as compiled by TSC with target=es5 is ok ether way
as it doesn’t rely on Object.defineProperty

myClass.js

var MyClass = (function () {
  function MyClass() {
      this.b = function (msg) { return myModule.func(msg); };
      this.c = myModule.func;
  }
  MyClass.prototype.a = function (msg) {
      return myModule.func(msg);
  };
  return MyClass;
}());

Guess what? With target=esnext, TSC generates perfectly fine code

myClass.js

import * as myModule from "./myModule";
export class MyClass {
    a(msg) {
        return myModule.func(msg);
    }
    b = (msg) => myModule.func(msg);
    c = myModule.func;
}

Basically, ES5 doesnt have classes at all, ES2018 does but defines each property using Object.defineProperty which is horrible. And ESNext is clean.