Skip to content

Instantly share code, notes, and snippets.

@OliverJAsh

OliverJAsh/foo.md

Last active Aug 20, 2019
Embed
What would you like to do?

Named namespace imports

If you're organising JavaScript/TypeScript code into modules, at some point you're going to need to consider how you're naming your imports and exports. At Unsplash, the issue we've found is that names either have too little information or too much—the former leads to naming conflicts and the latter leads to very long names.

Namespace imports are designed to help with this, but they have several disadvantages when compared with named imports. This article introduces named namespace imports, a technique which we've adopted at Unsplash to combine (as the name suggests) the best of both namespace imports and named imports.

Namespace imports

Imagine we have an API module which exports some functions corresponding to API endpoints:

// ./api/index.js
export const getPhoto = () => {
  /* … */
};

export const getUser = () => {
  /* … */
};
// ./app.js
import * as Api from "./api";

Api.getUser();

The good

We love namespace imports because they provide context. For example, instead of getUser(), which is ambiguous ("where are we getting a user from?"), we will see Api.getUser().

(The alternative would be to prefix the name of each named export, but that's tedious.)

We were initially worried about whether tree shaking would still work with namespace imports, but it does!

The bad

We hate namespace imports because, when compare to named imports, the developer UX suffers:

  • VS Code’s import suggestions won't help you if you type Api in a file where there is no import, leaving you to write out the import manually.
  • Similarly to default exports/imports, each instance of a namespace import must have its own name—unlike named imports, these imports do not share a common name. This makes it difficult to enforce consistent naming. If we use VS Code's rename functionality on a namespace import, it will only update the imported name in the current module.

Let's have our cake and eat it

We can combine the best of namespace imports and named imports using a technique called named namespace imports.

  1. Import as namespace
  2. Re-export the namespace as a named export
  3. Everywhere else, import the named namespace
// ./api/api.js
export const getPhoto = () => {
  /* … */
};

export const getUser = () => {
  /* … */
};
// ./api/index.js
import * as Api from "./api"; // 1
export { Api }; // 2
// ./app.js
import { Api } from "./api"; // 3

Api.getUser();

Tree shaking still works in Rollup. It doesn't work in webpack v4, but that's fixed in v5.

Can we do better?

The downside to named namespace imports is that you have to define an intermediary module to import and then re-export the namespace.

I wish ES modules had a way to say "export this whole module as a named export". Something like:

// ./api.js
export const getPhoto = () => {
  /* … */
};

export const getUser = () => {
  /* … */
};

// Pseudo code:
export * as Api;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.