Organizing a vast TypeScript project: Comparing Modules and Namespaces

As someone relatively new to TypeScript, I am currently working on a small prototyping framework for WebGl. During my project refactoring, I encountered challenges in organizing my code, debating between using modules or namespaces as both have their drawbacks.

This post does not focus on how to use these patterns, but rather on overcoming the issues each presents.

Current Approach: Utilizing Namespaces

Coming from a background in C#, utilizing namespaces felt like the natural route to take. Each class/module is allocated its appropriate namespace, and by providing the "outFile" parameter in the tsconfig.json file, everything gets combined into one large file. After compilation, the root namespace becomes a global object. However, project dependencies are not integrated, requiring manual inclusion of the necessary *.js files in the HTML (not ideal).

Example File:

namespace Cross.Eye {
    export class SpriteFont {   
        //code omitted
    }    
}

Example Usage (Ensure that the Cross namespace is loaded into the global namespace by including the respective JS file in the HTML):

namespace Examples {
    export class _01_BasicQuad {
        context: Cross.Eye.Context;
        shader: Cross.Eye.ShaderProgram;

        //code omitted
    }
}

Pros

  • Straightforward for those transitioning from C#/Java
  • Not dependent on filenames—changing file names won’t break the code
  • Easily refactorable: IDEs can efficiently rename namespaces/classes consistently throughout the codebase
  • Convenient: Adding a class to the project simply involves creating a file and declaring it in the desired namespace

Cons

For most projects, we recommend utilizing external modules and using namespaces only for quick demos or migrating old JavaScript code.

Source: https://basarat.gitbooks.io/typescript/content/docs/project/namespaces.html

  • A root namespace is always (?) a global object (not ideal)
  • Incompatible with tools like browserify or webpack, critical for bundling libraries with dependencies or when using custom code with the library
  • Considered bad practice if planning to release an npm module

Modern Practice (?): Modules

TypeScript supports ES6 Modules, which are regarded as innovative and preferred by many developers. The concept entails each file serving as a module, allowing clear definition of dependencies through import statements for efficient code packing by bundling tools. In my case, having one class per file doesn't seem to align well with the module pattern.

Post-refactor, my file structure is as follows:

Additionally, each folder contains an index.ts file enabling the importing of all classes within the folder via

import * as FolderModule from "./folder"

export * from "./AggregateLoader";
export * from "./ImageLoader";
export * from "./TiledLoader";
export * from "./XhrLoaders";
export * from "./XmlSpriteFontLoader";

Example File - The challenge becomes apparent here...

import {SpriteFont} from "./SpriteFont";
import {ISpriteTextGlyph, ISpriteChar} from "./Interfaces";
import {Event,EventArgs} from "../../Core";
import {Attribute, AttributeConfiguration} from "../Attributes";
import {DataType} from "../GlEnums";
import {VertexStore} from "../VertexStore";
import {IRectangle} from "../Geometry";
import {vec3} from "gl-matrix";

export class SpriteText {
    // code omitted
}

Example Usage - Noteworthy is the direct import of classes without navigating through namespaces.

import {
    Context,
    Shader,
    ShaderProgram,
    Attribute,
    AttributeConfiguration,
    VertexStore,
    ShaderType,
    VertexBuffer,
    PrimitiveType
} from "../cross/src/Eye";

import {
    Assets,
    TextLoader
} from "../cross/src/Load";

export class _01_BasicQuad {
    context: Context;
    shader: ShaderProgram;

    // code omitted.
}

Pros

  • Makes code more modular since it's no longer tied to namespaces
  • Compatible with bundling tools like browserify or webpack, which can bundle all dependencies together
  • Enhanced flexibility in importing classes without traversing namespace chains

Cons

  • Becomes laborious if each class resides in a separate file, necessitating repetitive import statements
  • Changing file names disrupts the code flow
  • Renaming classes does not automatically update imports (critical flaw depending on IDE; tested on VS Code)

In my opinion, both approaches exhibit flaws. Namespaces appear outdated and impractical for larger projects, while using modules poses inconvenience and detracts from some of TypeScript's core benefits. Ideally, I would construct my framework using the namespace pattern and export it as a module for comprehensive usage. Unfortunately, this integration seems improbable without resorting to unsightly workarounds.

Here's my question: How have you addressed these dilemmas? What strategies can be employed to mitigate the downsides imposed by each approach?

Update

Following further experience in TypeScript and JavaScript development, modules emerge as the predominant choice for about 90% of scenarios.

The remaining 10% predominantly involve legacy projects reliant on global namespaces, where TypeScript integration serves as a beneficial enhancement.

Many criticisms of modules have been alleviated—with enhanced IDE support being instrumental. Visual Studio Code now features automatic module resolution, streamlining the development process significantly.

Answer №1

tl;dr: Embrace the future with Modules, not the past.

In earlier versions of the ES6 modules specification, there was a concept of inline modules, which was later removed in September 2013. Interestingly, TypeScript had already implemented this idea back in 2012 with its internal modules. The final ES6 modules specification was released in July 2014 without inline modules. Subsequently, in July 2015, TypeScript version 1.5 made the switch from internal modules to namespaces to align with industry standards.

However, it's important to note that namespaces are considered a legacy feature by the TypeScript team and will not be part of ECMAScript moving forward. No significant updates have been made to TS namespaces since the introduction of the ECMAScript modules standard in July 2014.

Cons [of ES6 modules]

  • Managing multiple files with separate classes can become cumbersome due to repetitive import statements.
  • Changing file names may cause code breakages.
  • Refactoring class names might not automatically update imports (depending on your IDE).

There is optimism for improvement in these areas with upcoming IDE advancements. For example, WebStorm has already addressed the first issue mentioned above.

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

What is the best way to align a title above menu items within a Material UI app bar when using TypeScript and React?

Check out my current app bar design: app bar image Here is the inspiration for the app bar layout I'm aiming for (title above menu items): inspiration app bar goal This snippet showcases my code: import * as React from 'react'; // More cod ...

Error: Unable to access the 'prototype' property of an undefined object (inherits_browser.js)

After updating our app to a newer version of create-react-app, we started encountering the following error: https://i.sstatic.net/ILdsl.png This error seems to be related to inherits_browser.js, which is likely from an npm module that we are unable to id ...

Tips for finding the displayRows paragraph within the MUI table pagination, nestled between the preceding and succeeding page buttons

Incorporating a Material-UI table pagination component into my React application, I am striving to position the text that indicates the current range of rows between the two action buttons (previous and next). <TablePagination ...

Issue with ambient contexts error in TypeScript constructor

What is the correct way to create a constructor in TypeScript? I have been researching and trying different approaches, but it seems like the syntax has changed. Here is my latest attempt: car.d.ts declare class Car { constructor(public engine: string ...

What are the steps to installing Typescript on my computer?

npm ERROR! encountered code EACCES during installation npm ERROR! while trying to create a directory npm ERROR! at path /usr/local/lib/node_modules/typescript npm ERROR! with error number -13 npm ERROR! Error: EACCES: permission denied, mkdir '/usr/lo ...

Guide to mocking the 'git-simple' branchLocal function using jest.mock

Utilizing the simple-git package, I have implemented the following function: import simpleGit from 'simple-git'; /** * The function returns the ticket Id if present in the branch name * @returns ticket Id */ export const getTicketIdFromBranch ...

Solving the error message "Cannot find module '@angular/router/src/utils/collection' or its corresponding type declaration"

How do I troubleshoot this Error: src/app/metronic/orderByLocation/locationsByOneOrder/locationsByOneOrder.component.ts:7:25 - error TS2307: Cannot find module '@angular/router/src/utils/collection' or its corresponding type declarations.m 7 imp ...

Struggling to make even the most basic example work with TypeScript and npm modules

After stumbling upon this repository that made using npm modules within a Typescript program look easy, I decided to give it a try by forking it and making some changes. My goal was to add another package to get a better understanding of the process. So, I ...

Troubleshooting CORS errors in axios requests within a Next.js application

Encountering an issue while attempting to make an axios call to my API on a different localhost. How can this be resolved? The tech stack being used includes Next.js, TypeScript, and Axios. Below is the function which - although written poorly for testing ...

Can anyone provide guidance on setting up a TypeScript service worker in Vue 3 using the vite-plugin-pwa extension?

I am looking to develop a single-page application that can be accessed offline. To achieve this, I have decided to implement a PWA Service Worker in my Vue webapp using TypeScript and Workbox. I found useful examples and guidance on how to do this at . Ho ...

Encountering a TypeScript type error when returning a promise from a function

I currently have a scenario in which there is a function that checks if user whitelisting is required. If not, it calls the allowUserToLogin function. If yes, it then checks if a specific user is whitelisted. If the user is not whitelisted, an error is thr ...

Error: *** is not a valid function within a React element

I am encountering an issue while attempting to call a function of an object passed as an argument in a React component's constructor. The error message I receive is: Uncaught TypeError: _this.layout.getWbsLayout is not a function at new Wbs (Wbs. ...

I'm looking for some information on Typescript static functions - can anyone help me

Below is the code I am currently working with: class BaseClass { // includes a static method static someMethod() { } } class ChildClass extends BaseClass{ } class AnotherClass { protected variable: BaseClass; // Works fine with type &apos ...

Tips for creating dynamic alerts using mui v5 Snackbar

My goal is to call an API and perform several actions. After each action, I want to display the response in a Snackbar or alert. Despite iterating through the messages in a map, I'm only able to show the first response and none of the others. Here is ...

Retrieving display format or formatted value from an object with Moment.js

I am currently working on a project using Angular and Material2. Within this project, I have created a moment object in the following way: myDate = moment.utc(new Date()).format("YYYY-MM-DD HH:mm:ss"); This object is then passed as an argument to ano ...

Exploring methods for handling svgs incorporated in angular.js templates with webpack

I'm currently working on cache-busting my SVGs and other files like translation JSON. The issue I'm facing is that webpack does not seem to recognize imported svgs in the following format: <md-icon md-svg-src="assets/icons/ic-edit.svg">&l ...

The anticipated data type is derived from the attribute 'value' defined within the 'IntrinsicAttributes & ProviderProps<IProductContext>' type

During the creation of an e-commerce website, I decided to use React Typescript. However, I encountered an issue with Contexts while trying to export my state in the Provider value. Thank you for any assistance provided. ERROR Type '{ products: IP ...

Retrieving the latest status array by index using Typescript in Angular

Need help with this code logic. I am working on an array and function : import { Component } from '@angular/core'; @Component({ selector: 'my-app', templateUrl: './app.component.html', styleUrls: [ './app.compon ...

Error: Attempting to add types to an object returned from the .map function in JSX Element

When attempting to include the item type in the object returned from the .map function, I encountered a JSX error. I tried specifying item: JSX.Element as the Item type, but this didn't resolve the issue. Can someone provide clarity on this matter? Th ...

"Utilize React input event handler for enforcing minimum input

Is there a way to validate the minimum length of an input without submitting the form using the onKeyDown event? I've attempted to do so, but it seems to ignore the minLength property. I have a simple input field that doesn't need a form. Here&ap ...