Organizing code with modules or namespaces in TypeScript

In a library-based project, I am aiming to have all classes accessible under a specific namespace.

I seem to be overlooking a key step in organizing all classes within a namespace.

Let's say I have two classes in two separate folders.

  1. The initial step is to export all classes under a common library name or namespace.
  2. The next step involves utilizing these classes in a test folder by referencing them through their library name or namespace.

Expectation/Question: My goal is to access and utilize classes through a unified library name, such as new mylib.Foo() or new mylib.Bar()

Definition

src/a/Foo.ts

export module mylib {
    export class Foo {
    }
}

src/b/Bar.ts

export module mylib {
    export class Bar {
    }
}

src/index.ts

import { Foo } from './a/Foo'
import { Bar } from './b/Bar'

export { Foo, Bar }

Testing

test/a/Foo.spec.ts

import { mylib } from "../src";

test("constructor"), () = {
   const foo = new mylib.Foo() // expectation
}

test/b/Bar.spec.ts

test("constructor"), () = {
   const bar = new mylib.Bar() // expectation
}

Answer №1

If you are only interested in the final outcome you inquired about and do not necessarily need to utilize TypeScript's module syntax, you can simplify the process and explore a few options.

To simplify, you can remove the

export module mylib { /* ... */ }
wrappers in Foo.ts and Bar.ts, and simply have:

export class Foo
    // ...
}

and

export class Bar
    // ...
}

After this, it's a matter of how you want these classes to be imported from index.ts.

Module namespace object

The current setup in index.ts works well for creating two named exports, Foo and Bar. If you wish to access these via a mylib object, you can achieve that during import:

import * as mylib from "../src";
//     ^^^^^^^^^^

By using this import syntax, you are essentially saying "Import the module namespace object for the module and assign it to a binding called mylib." If the module namespace object does not exist, it will be created.

Once imported, you can use it as demonstrated in the question, for example, mylib.Foo.

However, with this approach, you could also use import { Foo } and/or import { Bar }. This flexibility may or may not be desirable based on your requirements.

Keep in mind that using import * as mylib could impact tree-shaking, which may be a consideration for your project. Ultimately, it is up to the consumer of your library to decide on this aspect.

Export an object

If you prefer not to go with the module namespace object approach, you could export an object directly from index.ts:

export const mylib = { Foo, Bar };

...or you could export a frozen object to prevent modifications to its properties:

export const mylib = Object.freeze({ Foo, Bar });

In this case, the import statement would be as shown in the question:

import { mylib } from "../src";

And you would access the classes using mylib.Foo and similar.

Keep in mind that this approach also hinders tree-shaking, not just based on how it's used, but at the library level as well. It might not be the most recommended option.

Both

Lastly, you could support both individual named exports and a single named object export. In this scenario, the exports in index.ts would look like this:

export { Foo, Bar };
export const mylib = Object.freeze({Foo, Bar});

This way, users have the choice to import Foo individually or the entire mylib object. This maintains tree-shaking efficiency while offering a convenient named export for the library.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for identifying the most effective element locator in the DOM with Playwright

Currently, I am in the process of incorporating Playwright tests for a website that supports multiple locales. The majority of the page content is dynamically generated from CMS content (Contentful). I am hesitant about using hardcoded text locators like ...

Modifying the value of an object key with Javascript

The information I am working with is structured as follows: obj = { pref: { language: 'English', } }; My goal is to update the language field to 'Spanish'. ...

Tips for creating TypeScript Google Cloud Functions using webpack

I'm currently facing a challenge while coding a Google Cloud Function using TypeScript. The concept involves having handler functions defined for various Cloud Functions in separate files within the source repository, along with some code that is shar ...

Enhancing Data Retrieval in Next.js: Implementing Typed Requests and Responses with the Fetch API

Context I've been developing a web application using Next.js and a custom Django Python backend, but I am struggling to find an efficient approach for making API requests from my frontend to the backend. My main goal is to centralize the logic for fet ...

TypeORM - Establishing dual Foreign Keys within a single table that point to the identical Primary Key

Currently, I am working with TypeORM 0.3.10 on a project that uses Postgres. One issue I encountered is while trying to generate and execute a Migration using ts-node-commonjs. The problem arises when two Foreign Keys within the same table are referencing ...

Utilize array mapping to alter the complete object

In my project using TypeScript (Angular 2), I am working on creating a "reset" method for an object array: cars [ { id: 1, color: white, brand: Ford, model: Mustang, ... }, ... ] Users have the ability to modify these objects, ...

Issues with slider functionality in a Next.js application styled with Tailwind CSS

"use client"; import React, { useState } from "react"; const textData = [ { id: 1, text: "Text 1 Description", }, { id: 2, text: "Text 2 Description", }, { id: 3, text: "Text 3 ...

Finding the key type in an interface

Here is a challenge... This is the code snippet from titleBarContainer.tsx: function TitleBarContainer() { const systemData = useSelector((state: RootState) => state.counter.systemData) const dispatch = useDispatch() const onChangeSystemDa ...

The dimensions of my Angular app have begun to unexpectedly expand

Currently in the process of developing an Angular application, I've encountered a frustrating issue. Each time I refresh the app using ng serve, the loading time seems to increase gradually. It can now take up to 10 seconds for changes to reflect in t ...

Angular UI failing to refresh despite Observable subscription activation

I'm facing an issue with my Angular page where the UI is not updating when the observable parameter from a service changes. I've experimented with storing the observable result in a flat value and toggling a boolean to update the UI, but none of ...

Can template literal types be utilized to verify if one numeric value is greater than another?

I am attempting to define the Record for migration functions, which use the direction of the migration as the key: v${number}-v${number}, Considering that these migrations are all UP, they need to be validated as v${first-number}-v${first-number + 1} and ...

Check out the computed typescript types

In my work with TypeScript types, I find myself frequently using Omit, Pick, and similar tools based on other types. While it generally gets the job done, I often struggle with readability when dealing with complex type manipulations. I am interested in f ...

Exploring Function Type in TypeScript: Utilizing both fat arrow and object literal type

Currently delving into the world of Typescript, I came across two methods for defining function types: using a fat arrow or an object literal. Here's an example: let myAdd1: (x: number, y: number) => number = function(x: number, y: number): n ...

I am looking for a guideline that permits me to restrict the use of a form validation tool

We have developed our own version of the Validators.required form-validator that comes with Angular 7, but now we need to switch to using CustomValidators.required. To enforce this change, we are considering banning the use of the old Validators.required b ...

The error message displayed in the Typescript Playground is stating that there is no property named 'values' on the type 'ObjectConstructor'

Could someone please take a look at this link? I'm encountering an error with the playground and I'm not sure why. Click here ...

What is the most appropriate form to use, and what factors should be considered in determining

Incorporating generics in typescript allows me to create a generic function in the following manner: Choice 1 declare function foo1<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } Alternatively, I have the option to omit the seco ...

What is the purpose of specifying the data types of my method parameters while I am incorporating an interface?

For instance: interface Foo { someProperty: Number someMethod?: (str: string) => void } class Bar implements Foo { someProperty = 42 someMethod (str) { console.log(this.someProperty) } } The str argument in someMethod() is clearly a str ...

What is the best way to obtain an error as an object when subscribing to an HTTP GET request

I am working on an asp.net core webApi along with an Angular9 WebApp. My goal is to retrieve the error in a subscribe as an object rather than just a string. this.http.post<TestSystem>(this.url, testsystem).subscribe((result) => { // do someth ...

Creating a Variety of Files in the Angular Compilation Process

Currently, I am developing an Angular project and faced with the task of creating various files during the build process depending on certain conditions or setups. I would appreciate any advice on how to accomplish this within the Angular framework. I att ...

Converting a typename to a type in TypeScript

Is there a way to cast a string with a typename to a type without an explicit mapping in order to use it for an angular component factory? When there is a mapping, the process is straightforward: public readonly typeMap: Map<string, Type<{}>> ...