With the use of third-party libraries in enterprise software increasing, we often encounter the problem of polluted global namespaces, causing name collision between components in the global namespace. Therefore, we need to organize blocks of code using namespaces so that variables, objects, and classes are uniquely identified.
In this article, we will discuss namespaces, when you’ll need them, and how to use them to enhance the organization of your TypeScript code.
Namespaces are paradigm of organizing code so that variables, functions, interfaces, or classes are grouped together within a local scope in order to avoid naming conflicts between components in the global scope. This is one of the most common strategies to reduce global scope pollution.
While modules are also used for code organization, namespaces are easy to use for simple implementations. Modules offer some additional benefits like strong code isolation, strong support for bundling, re-exporting of components, and renaming of components that namespaces do not offer.
Namespaces have these advantages:
Using namespaces while working with some external libraries will require an implicit implementation of dependency between your code and these libraries. This results in the stress of managing the dependencies yourself so that they are loaded correctly, because the dependencies can be error-prone.
If you find yourself in such a situation, using modules will save you the stress.
For Node.js applications, modules are recommended over namespaces since modules are the de facto standard for encapsulation and code organization in Node.
Modules are recommended over namespaces when dealing with non-JavaScript content since some module loaders such as SystemJS and AMD allow non-JavaScript content to be imported.
When working with a codebase that is no longer engineered but continually patched, using namespaces is recommended over modules.
Also, namespaces come in handy when porting old JavaScript code.
Now that we have a shared understanding of what TypeScript namespaces are and why we need them, we can take a deeper dive into how to use them.
Given that TypeScript is a superset of JavaScript, it derives its namespace concept from JavaScript.
By default, JavaScript has no provision for namespacing because we have to implement namespaces using IIFE (Immediately Invoked Function Expression):
var Vehicle; (function (Vehicle) { let name = "car"; })(Vehicle || (Vehicle = {}));
This is so much code for defining a namespace. Meanwhile, TypeScript does things differently.
In TypeScript, namespaces are defined using the namespace
keyword followed by a name of choice.
A single TypeScript file can have as many namespaces as needed:
namespace Vehicle {} namespace Animal {}
As we can see, TypeScript namespaces are a piece of syntactic cake compared to our JavaScript implementation of namespaces using the IIFE.
Functions, variables, and classes can be defined inside a namespace as follows:
namespace Vehicle { const name = "Toyota" function getName () { return `${name}` } } namespace Animal { const name = "Panda" function getName () { return `${name}` } }
The above code allows us to use the same variable and function name without collision.
In order to access functions or classes outside their namespaces, the export
keyword must be added before the function or class name as follows:
namespace Vehicle { const name = "Toyota" export function getName () { return `${name}` } }
Notice that we had to omit the export
keyword with the variable because it should not be accessible outside the namespace.
Now, we can access the getName
function as follows:
Vehicle.getName() //Toyota
TypeScript allows us to organize our code using nested namespaces.
We can create nested namespaces as follows:
namespace TransportMeans { export namespace Vehicle { const name = "Toyota" export function getName () { return `${name}` } } }
Notice the export
keyword before the Vehicle
namespace. This allows the namespace to be accessible outside of the TransportMeans
namespace.
We can also perform deep nesting of namespaces.
Our nested namespaces can be accessed as follows:
TransporMeans.Vehicle.getName() // Toyota
For deeply nested namespaces, the namespace alias comes in handy to keep things clean.
Namespace aliases are defined using the import keyword as follows:
import carName= TransporMeans.Vehicle; carName.getName(); //Toyota
Namespaces can be shared across multiple TypeScript files. This is made possible by the reference
tag.
Considering the following:
//constant.ts export const name = "Toyota" //vehicle.ts <reference path = "constant.ts" /> export namespace Vehicle { export function getName () { return `${name}` } }
Here, we had to reference the constant.ts
file in order to access name
:
//index.ts <reference path = "constant.ts" /> <reference path = "vehicle.ts" /> Vehicle.getName() // Toyota
Notice how we started our references with the highest-level namespace. This is how to handle references in multi-file interfaces. TypeScript will use this order when compiling the files.
We can instruct the compiler to compile our multi-file TypeScript code into a single JavaScript file with the following command:
tsc --outFile index.js index.ts
With this command, the TypeScript compiler will produce a single JavaScript file called index.js
.
In order to build scalable and reusable TypeScript applications, TypeScript namespaces are handy because they improve the organization and structure of our application.
In this article, we’ve been able to explore namespaces, when you need them, and how to implement them. Check out TypeScript Handbook: Namespaces for more information about namespaces.
LogRocket lets you replay user sessions, eliminating guesswork by showing exactly what users experienced. It captures console logs, errors, network requests, and pixel-perfect DOM recordings — compatible with all frameworks, and with plugins to log additional context from Redux, Vuex, and @ngrx/store.
With Galileo AI, you can instantly identify and explain user struggles with automated monitoring of your entire product experience.
Modernize how you understand your web and mobile apps — start monitoring for free.
Would you be interested in joining LogRocket's developer community?
Join LogRocket’s Content Advisory Board. You’ll help inform the type of content we create and get access to exclusive meetups, social accreditation, and swag.
Sign up nowBuild a React-based AI image generator app offline using the Hugging Face Diffusers library and Stable Diffusion XL.
Get up to speed on Google’s latest breakthrough with the Gemini 2.5 model and what it means for the future of frontend AI tools.
In this article, we’ll explore the best Rust frameworks for web development, including Actix Web, Rocket, Axum, warp, Leptos, Cot, and Loco.
cursor
propertyA single line of CSS can change how users feel about your UI. Learn how to leverage the cursor property to signal intent, improve interaction flow, and elevate accessibility.
2 Replies to "Organizing TypeScript code using namespaces"
What I don’t understand, in 2021 why would you not use modules in every scenario. You’ve said for codebase that is only being patched namespaces are preferable over modules, but who said that, and why is that? I’d prefer modules regardless of where the code is in its lifecycle and I cannot think of a disadvantage in keeping everything tightly scoped. I think you need to expand on that as in your whole article it’s the only part that gives any reason why you would use this strategy atall.
Namespaces is a legacy feature which wouldn’t make it if typescript was created today.