TypeScript Modules: How do Modules Work?

Learn via video courses
Topics Covered

Overview

Websites are turning complex applications and the code complexity is increasing. The dependencies between the js files should be tracked and included in the correct order so that the website works properly. In this case, we are required to use modules. A module can be imported by another module using the module loader. Module loader is responsible for finding and executing all the dependencies of a module before executing the loader. The most common module loader that is used is the CommonJs module loader for Node.js and Require.js is majorly used for Web-based applications.

Internal Module

The Internal module used to come in the earlier version of TypeScript. The internal module was used to logically group classes, interfaces, and functions into a single unit and can be exported into another module. This kind of grouping is called a namespace in the latest version of TypeScript. Hence, they are outdated rather we can use a namespace. They are still supported in TypeScript but it is recommended to use a namespace over internal modules.

Syntax of Internal Module

Syntax of Namespace

External Module

External modules are also known as a module. When we use a modular approach, there can be difficult while handling an application consisting of hundreds of files. The external module is used to specify load dependencies between multiple external js files. If there is only one js file, in that case, this module will not be applicable. We can use browser script tags to express traditional dependency management between the JavaScript files. There are two ways to load dependent js files from a single main JavaScript file.

Module Declaration

A module can be declared by using the export keyword. The syntax for the declaration of the module is given as:

We can use the declared module in other files using the import keyword. The file/module name is specified without an extension.

Example

Module creation: sub.ts

When we will access the module in another file by using the import keyword: app.ts

Compiling and Executing Modules

To compile and execute the module, we will open the terminal and go to the location where you stored the project. Now, we will type the following command in the terminal window.

Output

Compiling and Executing Multiple Modules

To compile and execute multiple modules, we will open the terminal and go to the location where you stored the project. Now, we will type the following command in the terminal window.

Output

Creating Modules in TypeScript with Export

We can create modules in TypeScipt using the TypeScript module syntax. The files in TypeScript are considered global scripts. This simply depicts that any function, class, variable, or constructor declared in the file can be made global. As soon as we use modules in a file, the file becomes module-scoped, and it will no longer be global. To achieve this, we are required to create a class named NewClass. We will create a new directory called src/ in the root of the project:

The directory will be set as the root directory (rootDir) in the tsconfig.json file. In this folder, a file will be created namely Vector.ts. We need to open the file in a text editor and write the below code:

Explanation

In the above code snippet, we have declared a class named Vector which is created by passing two numbers as parameters, set as the properties a and b. As our file is currently not using modules, the class Vector is globally scoped. To convert the file into a module, we have to export our Vector class:

Explanation

The file src/Vector.ts is now a module that has a single export: the Vector file. Now, we can create a new class named Vector2. A file named Vector2 file will be created inside the src/ directory. We need to open the file in a text editor and write the below code:

Explanation

This code will create a class similar to Vector but with an extra property c that stores the vector in three dimensions. The export keyword has already made the above file its module. Now, we will have two files Vector.ts and Vector2.ts that both are modules. Each file will have a single export which is the class for the vector they represent.

Using Modules in TypeScript with Import

When we work with modules in TypeScript, a common scenario occurs when a single file collects multiple modules and re-exports them as one module. To demonstrate this, a file will be created Vectors.ts inside the src/ directory, then write the following:

Explanation

To import another module, we can use the relative path to the file in the import statement. In the above case, we can import both modules from ./Vector and ./Vector2which are the relative paths from the current file to the files src/Vector.ts and src/Vector2.ts.

Now, we have exported the vectors, we can now actually re-export them in a single module with the given syntax:

Explanation

The export {} allows us to export multiple identifiers. In the above case, we have exported Vector, and Vector2 classes using a single export declaration. We can also use two separate export statements:

Explanation

This snippet will have the same meaning as the previous code snippet. As our src/Vectors.ts only imports two classes to later re-export them, we can use a briefer form of the syntax:

Explanation

The import statement is implicit in the above declaration and the TypeScript compiler will include the declaration automatically. Then the files will be immediately exported with the same name.

Now, we have exported two vector classes from the src/Vectors.ts file, a new file will be created namely src/index.ts, and then the following code will be written in it:

Explanation

In the above code, we have imported both vector classes from the src/Vectors.ts file that uses the relative path ./Vectors. Then, we created a few vector instances using the addNum method to add them together. We can make use of a different alias when importing named exports, which can be helpful to avoid naming collisions inside a file.

Explanation

In the above snippet, we have used the as keyword to set aliases Vec and Vec2 for the imported classes. We can also make use of the following syntax to import everything into a single variable:

Explanation

In the above snippet, we can use the import * as to import everything that is exported by a module into a single variable. Also, we have to change the way we have used the Vector and Vector2 classes, as they are now available inside the Vectors object that is created during the import.

If we save the file and compile the project using tsc:

The TypeScript compiler will create the out/ directory along with the compilations.outDir option we set in the tsconfig.json file, then we will populate the directory with JavaScript files. If we open the index.ts file in the text editor, then it will look like this:

Explanation

As the compilerOptions.module option in our tsconfig.json file is set to Commonjs, then the TypeScript compiler creates the code that is compatible with the Node.js environment. The require function is used to load other files as modules.

Using Default Exports

In this section, we will make use of default exports to export a value from a module that sets a specific export to the assumed import from a module. This will simplify the code when we import the files.

Every file can have at most a single default export. To change your export to a default export, add the following default:

Now, we can do the same in the src/Vector2.ts file:

To import the default exports, save the files and open the src/Vectors.ts file and change its contents to the following:

Explanation

For both imports, we are just giving a name to our import, instead of destructuring to import a specific value. This will automatically import the default export of each module. Each module that has a default export also has a special export known as default that can be used to access the default exported value. We can also use the named export to use the export ... from shorthand syntax.

Now, we are re-exporting the default export of each module under a specific name.

Re-exports

In TypeScript, modules extend other modules and partly reveal some of their features. A re-export will neither introduce a local variable nor import locally. In that case, some features can be re-exported either using their original name or we can rename the original name to re-export.

Example

Module creation: Module.ts

Create a re-exports module: re-export.ts

Explanation

In the below snippet, the name of the Multiply export class will be changed to plus using {Multiply as plus}. The re-exporting is useful in assigning a more meaningful name to an export that increases the readability of the program.

We will access the module in another file by using the import keyword: app.ts

Explanation

To compile and execute the above module, we will open the terminal and go to the location where you stored the project. Now, we will type the following command in the terminal window.

Output

Conclusion

  • Modules help us in releasing cleaner applications and understanding dependencies between the components.
  • Native modules are not well supported yet, hence we can use modules with System.js or require.js and migrate to native modules once the support will be good.
  • TypeScript has the same module concept as the ES6 module. Modules can have both declarations and code.
  • Modules help us to organize our application code effectively and concisely, as they are the fundamental part of having a structured code base that is easy to extend and maintain.
  • By using the import and export options available in TypeScript, we can ensure the modularity of code and compatibility with a greater JavaScript environment.
  • We can use the import statement to access exports from other modules and the export statement to export variables, functions, classes, and interfaces from a module.