2

I want to design a TypeScript (2.7) module for accessing external IS, let's call it InfoSys. I used the following approach.

I created info-sys.ts which defines a API class and related interfaces and enums, like:

class Api {
    constructor(private endpoint: Endpoint) {
        // ...
    }
}

enum Endpoint {
    CONTACTS = "contacts"
}

interface Contact {
    name: string;
}

Now I want to export all the stuff under specific name. So I appended the export statement:

export const InfoSys = {
    Api,
    Endpoint,
    Contact
};

When I try to use the module in another file, like:

import { InfoSys } from "info-sys";

// this line throws error: "Cannot find namespace 'InfoSys'"
private api: InfoSys.Api;

// but this line is ok
api = new InfoSys.Api(InfoSys.Endpoint.CONTACTS);

The way that works is the following - to export every piece individually:

export class Api {
    constructor(private endpoint: Endpoint) {
        // ...
    }
}

export enum Endpoint {
    CONTACTS = "contacts"
}

export interface Contact {
    name: string;
}

and import them all to a single variable:

import * as InfoSys from "info-sys";

But the name of the variable can be whatever. It is not critical for functionality but I want to force developers, who will use the info-sys module, to use a specific name while accessing it (for easier readability and maintainability). How to properly design such module?

1 Answers1

0

You can use namespace:

export namespace InfoSys {
  Api,
  Endpoint,
  Contact
};

In general, this approach should be avoided. But in your case, it is fine as you are delivering things that are tightly related.

If Api is the single entry point to all these, I would also recommend this:

export class InfoSysApi { ... }
export namespace InfoSysApi {
  export enum Endpoint = { ... }
  export interface Contact { ... }
}  

UPDATE: To make sure I get the point through, DON'T do the following:

export namespace Foo {
  export function X() { return 'x' }
  export function Y() { return 'y' }
}

Only use export namespace to export "tugged in types", not values.

In TypeScript handbook: https://www.typescriptlang.org/docs/handbook/declaration-merging.html

Although the table says namespace can contain values, it is considered bad practice if you are writing ESM (import/export).

Namespace and ESM are two different mechanisms to achieve similar result. Don't mix them together.

unional
  • 14,651
  • 5
  • 32
  • 56
  • Thank you kindly @unional for your answer and time. The namespacing was the first approach what we used. But after including the linting to our process we left this pattern such something evil and freedom restricting (after googling why this rule is used). We don't want to limit the mind of our coders but we need to make our code easier maintainable. And in this case the "the end justifies the means" is becoming to be legitimate and fair, isn't it? :) Thank you again for your vote. – Tomáš Klíma Feb 28 '18 at 07:53
  • Linting is good. I have pretty strict linting rules too. But don't let it hinders your productivity. Cheers. :) – unional Feb 28 '18 at 07:57