28

Lets say I have a typescript file Utils with a bunch of exported functions:

export function utilOne(){}
export function utilTwo(){}

I added index.d.ts file to this folder where I export * from the Utils file:

export * from './Utils';

In my other classes I'd like to access functions utilOne and utilTwo via utils namespace, like:

utils.utilOne();

I know that I can import it like this:

import * as utils from "./Utils";

However, as I will be using utils a lot, I would like to be able to export utils in a namespace, something like:

export {* as utils} from './Utils';   // this doesn't work

and then use:

import * from "./Utils";

However the export {* as utils} doesn't work. I could put all the functions of Utils to a module "utils" and export it, but I am not sure if this is a good practice. Is there a proper way to do this?

4 Answers 4

14

Since TC39/ecma 262 the feature you probably need was merged. So now you can export the module itself like this:

// module.ts

export interface Foo{
    bar: string;
}

export function A(){
  // implementation
}

export function B(){
  // implementation
}

export * as MyModule from './module';

And then use it like a module that contains his own context:

import { MyModule } from './module';

type MyType = MyModule.Foo;

MyModule.A();
MyModule.B();

It seems to me it depends on TS version and Webpack if you are using it.

  • Webpack 5 and TS v4.1.2 works good.
  • Vite js - works good too. Did not check tree-shaking yet, but looks it should work fine.

UPDATE:

In the case above everything is good, however, the aggregating module exports itself and if you try to check this with madge --circular [directory] it will show that there is a circular dependency.

So, in order to avoid this we can follow Mmdn web docs and add intermediate file like barrel.ts

├── src
│   ├── main.js
│   ├── myModule
│   │   ├── functionA.js
│   │   ├── functionB.js
│   │   ├── barrel.js
│   │   ├── index.js

// -- myModule/functionA.js --
export function A(){
  return 'this is module A' 
}
  
// -- myModule/functionB.js --
export function B(){
  return 'this is module B' 
}

// -- myModule/barrel.js --
export * from "./functionA.js";
export * from "./functionB.js";

// -- myModule/index.js
export * as MyModule from './barrel.js';

// -- main.js --
import { MyModule } from "./myModule";
console.log(MyModule.A); // this is module A
console.log(MyModule.B); // this is module B
Sign up to request clarification or add additional context in comments.

Comments

6

Caveats aside, depending on your project, one can accomplish this export utilizing a layer of indirection.

./utils/internal/utils.ts

export function utilOne(){}
export function utilTwo(){}

./utils/utils.ts

import * as utils from './internal/utils';
export utils;

./test.ts

import { utils } from './utils/utils';

utils.utilOne();

Caveats:

As noted, this is a questionable practice, and the desire to namespace may be indicative of a code-smell. It may also negatively impact the tree-shakability of your library and resulting projects.

Note: This structure is inspired by RxJS.

Comments

5

import * from

No. Global imports are considered bad practice even in languages that support them. (e.g. python Why is "import *" bad?)

JavaScript / TypeScript doesn't support it. After all its pretty useful to see foo.bar and know that bar is coming from foo instead of bar and having no clue where bar is coming from (without cloning and analyzing the whole project). 🌹

4 Comments

I think this is a good question other than the last line and worth answering despite the request for bad practice. They could just as easily have asked for the ability to do import { utils } from './Utils' I have a library made up of a bunch of files. I would like the exports from the library to be namespaced by file rather than all jammed together into a single module wide namespace. I believe this is the same as what the OP wants.
That does not answer the question at all, it's even out of topic. The OP is exactly asking for obj like access to his modules and is asking about glob exports not imports.
I would argue that global/wildcard imports are only bad practice for non-interpreted languages, or interpreted languages without a well defined specification for such imports (i.e. javascript before ES6). For interpreted languages like javascript with a well defined specification, it is a simple thing for static analysis tools (Typescript) to resolve wildcard imports just as deterministically and quickly as named imports.
Telling people "No, it's bad practice" is seldom useful without meeting the need implied in some other way. Also, lots of things that are bad practice have situations where they are useful (e.g., quick tests of an idea).
1

Use an actual namespace:

namespace Utils {
  export function utilOne(){}
  export function utilTwo(){}
}

export default Utils;

You can then import it as:

import utils from "./Utils";
// ...
utils.utilOne();

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.