TypeScript Reference : Modules

TypeScript Reference : Modules

Modules are an essential part of the TypeScript language. They provide a way to organize and encapsulate code into reusable and manageable units. In TypeScript, modules are defined using the export and import keywords.

The export keyword is used to make a module or a specific function, class, or variable within a module accessible to other parts of the program. By default, everything in a module is private and can only be accessed within the module itself. However, by using the export keyword, you can make it public and available for other modules to use.

The import keyword is used to bring functionality from other modules into the current module. This allows you to use code from other files or libraries in your TypeScript program. Import statements specify the module and the specific functionality to import, and can be used to bring in individual functions, classes, or variables from a module.

Modules can be organized into a hierarchy using the naming conventions of dot-separated names. This allows you to create nested modules and have a clear structure for your code. TypeScript also supports the concept of default exports, which allows you to export a single value or functionality as the default for a module. This can simplify the import syntax when using that functionality in other modules.

Table of Contents

Exporting and Importing Modules in TypeScript

In TypeScript, modules are a way to organize and reuse code. Modules allow you to encapsulate related code into a separate unit, making it easier to manage and maintain your codebase.

Exporting Modules

When you want to make code in a module available to other parts of your program, you need to export it. To export a module in TypeScript, you use the export keyword.

Here’s an example:

export function add(num1: number, num2: number): number {

return num1 + num2;

}

export const pi: number = 3.141592653589793238;

In the example above, the add function and the pi constant are exported and can be accessed from other modules using the import syntax.

Importing Modules

To use code from an exported module in another module, you need to import it. In TypeScript, you can import modules using the import keyword.

Here’s an example:

import { add, pi } from './math';

console.log(add(2, 3)); // Output: 5

console.log(pi); // Output: 3.141592653589793238

In the example above, we import the add function and the pi constant from the math module using the relative path './math'. We can then use the imported functions and constants in our code.

Default Export

In addition to named exports, TypeScript also supports default exports. Default exports allow you to export a single value or a default object from a module.

// math.ts

export default function multiply(num1: number, num2: number): number {

return num1 * num2;

}

// main.ts

import multiply from './math';

console.log(multiply(2, 3)); // Output: 6

In this example, the multiply function is exported as the default export from the math module. We then import it using the import multiply from './math' syntax.

It’s important to note that you can only have one default export per module.

Conclusion

Conclusion

Exporting and importing modules in TypeScript allows you to organize your code into reusable units and easily share code between different parts of your program. By using modules, you can make your code more maintainable and scalable.

Default Exports in TypeScript Modules

Introduction

In TypeScript, modules are used to organize code into smaller, reusable units. They allow you to encapsulate related functionality and expose only what is necessary to the outside world. One useful feature of TypeScript modules is the ability to export a default value from a module.

Default Exports

In a TypeScript module, you can specify a default export using the `export default` syntax. This allows you to provide a single, default value for the module. Only one default export is allowed per module.

For example, let’s say we have a module called `mathUtils.ts` that contains various utility functions:

// mathUtils.ts

export default function add(a: number, b: number): number {

return a + b;

}

export function subtract(a: number, b: number): number {

return a - b;

}

In this example, we are exporting the `add` function as the default export from the `mathUtils` module. This means that when another module imports from `mathUtils`, the default export will be returned.

You can import the default export using any name you want:

// app.ts

import myAddFunction from './mathUtils';

console.log(myAddFunction(2, 3)); // Output: 5

The `import` statement in this example imports the default export from `mathUtils` and assigns it to the variable `myAddFunction`. We can then use `myAddFunction` to call the `add` function from the `mathUtils` module.

Named Exports vs Default Export

It’s important to note that you can have a combination of named exports and a default export in a TypeScript module. This allows you to export individual functions or values, as well as provide a default export. However, when importing from a module with both named exports and a default export, you need to use different import syntax:

  • To import the default export, use the `import myDefaultExport from ‘module’` syntax.
  • To import named exports, use the `import { namedExport } from ‘module’` syntax.

For example:

// mathUtils.ts

export default function add(a: number, b: number): number {

return a + b;

}

export function subtract(a: number, b: number): number {

return a - b;

}

// app.ts

import myAddFunction, { subtract } from './mathUtils';

console.log(myAddFunction(2, 3)); // Output: 5

console.log(subtract(5, 3)); // Output: 2

In this example, we are importing both the default export (`myAddFunction`) and the named export (`subtract`) from the `mathUtils` module. We can use both of them in our `app.ts` file.

Conclusion

Default exports in TypeScript modules provide a convenient way to export a single value from a module. They make it easy to import and use the default export in other parts of your code. Additionally, you can use a combination of named exports and a default export in a module to export multiple functions or values.

Namespace Imports in TypeScript Modules

Namespace imports in TypeScript modules allow you to organize and encapsulate your code into logical units called namespaces. A namespace is a container for a set of related functions, classes, interfaces, and other objects, which helps prevent naming conflicts and improves code organization.

Namespace imports can be especially useful when working with large projects that involve multiple contributors. They provide a way to structure your codebase and avoid potential naming clashes between different parts of the project.

Importing a Namespace

To import a namespace in TypeScript, you use the import keyword followed by the namespace name:

import * as MyNamespace from './myNamespace';

This syntax imports the entire namespace MyNamespace from the file myNamespace.ts, located in the same directory as the current file.

After importing the namespace, you can access its members using the dot notation:

MyNamespace.myFunction();

let myVariable: MyNamespace.MyClass = new MyNamespace.MyClass();

Renaming a Namespace on Import

If you want to give a different name to the imported namespace, you can use the as keyword:

import * as ns from './myNamespace';

Now you can access the namespace members using the new name:

ns.myFunction();

let myVariable: ns.MyClass = new ns.MyClass();

Importing Specific Members

Importing Specific Members

If you only need to import specific members of a namespace, you can use the import keyword followed by the member name:

import { myFunction } from './myNamespace';

This syntax imports only the myFunction from the myNamespace file. You can then use it directly without accessing the namespace:

myFunction();

Summary

Namespace imports in TypeScript modules provide a way to organize and encapsulate code into logical units called namespaces. They help prevent naming conflicts and improve code organization. You can import a namespace using the import keyword followed by the namespace name. Renaming the namespace on import and importing specific members are also possible.

Understanding Module Resolution in TypeScript

Introduction

TypeScript allows developers to divide their code into reusable modules, making it easier to manage and organize large codebases. When working with modules, it’s important to understand how TypeScript resolves module dependencies. Module resolution is the process by which TypeScript determines the location of a referenced module in order to import it into the current file.

Absolute vs Relative Paths

Module resolution in TypeScript can be done using either absolute or relative paths. Absolute paths start with a forward slash (`/`) and are resolved relative to the project root. Relative paths, on the other hand, are resolved relative to the current file.

Classic Resolution Algorithm

The classic resolution algorithm is the default module resolution strategy in TypeScript. It uses the `baseUrl` and `paths` configuration options specified in the `tsconfig.json` file to resolve module names.

  • The `baseUrl` option specifies the root directory for resolving non-relative module names.
  • The `paths` option maps module names to specific paths or patterns to use when resolving modules.

When resolving a module, TypeScript will first check if the module name matches any of the patterns specified in the `paths` option. If a match is found, it will use the corresponding path to resolve the module. If not, it will fall back to using the `baseUrl` option to resolve the module.

Node.js Module Resolution

In addition to the classic resolution algorithm, TypeScript also includes support for Node.js module resolution. This allows TypeScript to resolve modules using the same rules as Node.js, making it easier to work with existing Node.js projects.

The Node.js module resolution algorithm is based on the concept of package folders, which are folders that contain a `package.json` file. When resolving a module, TypeScript will search for a `package.json` file in the parent directories of the current file. Once found, it will use the `main` field of the `package.json` file to determine the entry point of the module.

Triple-Slash Reference Directives

In addition to module imports, TypeScript also supports triple-slash reference directives, which are used to reference external modules without importing them. The syntax for a triple-slash reference directive is as follows:

“`typescript

///

“`

Triple-slash reference directives are resolved relative to the current file, similar to relative paths. However, unlike module imports, they do not create a runtime dependency and are used primarily for type checking and intellisense.

Conclusion

Understanding module resolution in TypeScript is crucial for managing dependencies and building scalable applications. By knowing how TypeScript resolves module names, developers can take advantage of the various module resolution strategies to organize their code effectively.

Declaring External Modules in TypeScript

Introduction

TypeScript allows you to organize your code into modules, which can be either internal or external. In this article, we will focus on external modules, also known as dependency declarations.

What are External Modules?

External modules in TypeScript are used to declare dependencies on external libraries or modules. They provide a way to organize code in larger projects and allow for better code reusability. External modules can be imported and exported, enabling you to use functionality from other modules in your code.

Importing External Modules

To import an external module in TypeScript, you can use the import keyword followed by the module name. You can use the import statement to import specific parts of the module or the entire module.

For example, to import a specific function from an external module called “myModule”, you can use the following syntax:

import { myFunction } from 'myModule';

You can also use the * character as a wildcard to import all exports from the module. For example:

import * as myModule from 'myModule';

Exporting External Modules

To export functionality from an external module in TypeScript, you can use the export keyword followed by the name of the function, class, or variable you want to export.

For example, to export a function called “myFunction” from an external module, you can use the following syntax:

export function myFunction() {

// function logic here

}

You can also use the default keyword to export a default value from a module. This is useful when you want to provide a single, default export from an external module.

Using External Modules

To use an external module in your TypeScript code, you need to declare a reference to the module using the /// <reference path="..." /> syntax.

For example, if you have an external module named “myModule” located in a file called “myModule.d.ts”, you can declare a reference to it like this:

/// <reference path="myModule.d.ts" />

Once you have declared the reference, you can use the exported functionality from the module in your TypeScript code.

Conclusion

With external modules in TypeScript, you can easily declare dependencies on external libraries or modules, import functionality from those modules, and export functionality from your own modules. This helps in organizing code in larger projects, improving code reusability, and enhancing modularity.

Ambient Modules in TypeScript

TypeScript allows you to declare ambient modules to represent existing JavaScript modules that are not written in TypeScript. Ambient modules provide a way to describe the shape of external modules and their members without actually defining their implementation.

Declaring an Ambient Module

To declare an ambient module in TypeScript, you need to use the keyword declare followed by the module name. Ambient modules should be declared in a separate file with the .d.ts extension, which stands for declaration file.

Here’s an example of how to declare an ambient module:

  1. Create a new file with the extension .d.ts (e.g. myModule.d.ts).
  2. Inside the file, use the declare keyword followed by the module name:

declare module 'myModule' {

// Module members here

}

Once you have declared an ambient module, you can reference it in your TypeScript code and import its members using the import statement.

Using Ambient Modules

To use an ambient module in your TypeScript code, you need to reference the declaration file that represents the module. You can do this using a ///<reference> directive at the top of your TypeScript file.

///<reference path="myModule.d.ts" />

import { someFunction } from 'myModule';

// Use the imported function

someFunction();

In the above example, the ///<reference> directive tells the TypeScript compiler to include the declaration file for the myModule ambient module. This enables you to use the imported function someFunction in your code.

Conclusion

Ambient modules in TypeScript allow you to describe the shape of existing JavaScript modules without defining their implementation. By declaring ambient modules and referencing their declaration files, you can use external modules in your TypeScript projects and benefit from the type checking and IntelliSense features provided by the TypeScript compiler.

Using Wildcard Imports in TypeScript Modules

Wildcard imports in TypeScript modules allow you to import an entire module’s contents under a single name, with the help of the asterisk (*) symbol. This feature can be particularly useful when you want to import multiple items from a module without explicitly specifying each one.

Wildcard Import Syntax

To use wildcard imports in TypeScript, you can use the following syntax:

import * as moduleAlias from "module-name";

Here, moduleAlias is the name you give to the imported module, while module-name represents the name of the module you want to import.

Using Wildcard Imports

After importing a module using the wildcard import syntax, you can access the module’s contents through the moduleAlias. For example, let’s say you have a module named “math” containing various sub-modules and functions:

// math.ts

export module Addition {

export function add(a: number, b: number): number {

return a + b;

}

}

export module Subtraction {

export function subtract(a: number, b: number): number {

return a - b;

}

}

export function multiply(a: number, b: number): number {

return a * b;

}

In another file, you can import the entire “math” module using a wildcard import, like this:

// main.ts

import * as math from "./math";

console.log(math.Addition.add(1, 2)); // Output: 3

console.log(math.Subtraction.subtract(5, 3)); // Output: 2

console.log(math.multiply(2, 4)); // Output: 8

By using the math alias, you can access the sub-modules and functions of the “math” module without explicitly importing them one by one.

Benefits of Wildcard Imports

Wildcard imports can provide several benefits in your TypeScript modules:

  • Convenience: Instead of importing each individual item from a module separately, you can import them all at once using a wildcard import.
  • Clarity: Wildcard imports make it clear that you are importing multiple items from a module without the need for repetitious code.
  • Scalability: If the module you are importing adds or removes items in the future, your wildcard import will still work without the need for modifying your import statements.

However, it’s important to note that using wildcard imports can increase the size of your compiled JavaScript bundle since it imports the entire module, even if you only need a few items. Therefore, it’s recommended to use wildcard imports responsibly and only when necessary.

Conclusion

The wildcard import syntax in TypeScript allows you to import all items from a module under a single alias. This can provide convenience, clarity, and scalability in your codebase. However, it’s important to be mindful of the size of your compiled JavaScript bundle when using wildcard imports.

Module Aliases in TypeScript

Module aliases are a useful feature in TypeScript that allows you to create shorter and more readable import statements for your modules.

Syntax

Module aliases are created using the import statement and the as keyword. The syntax is as follows:

import * as aliasName from 'modulePath';

import { propertyName as aliasName } from 'modulePath';

Here, aliasName is the name you want to give to the module or property you are importing, and modulePath is the path to the module file you want to import.

Example

Let’s say you have a module called mathUtils that exports a function called addNumbers:

// mathUtils.ts

export function addNumbers(a: number, b: number): number {

return a + b;

}

If you want to import this function using a module alias, you can do it like this:

// app.ts

import { addNumbers as add } from './mathUtils';

console.log(add(2, 3)); // Output: 5

By using the as keyword, we can give the imported addNumbers function the alias add. This allows us to use a shorter and more readable name when calling the function.

Benefits of Using Module Aliases

There are several benefits to using module aliases in TypeScript:

  1. Shorter import statements: Module aliases allow you to define shorter and more concise import statements, which can make your code easier to read and understand.
  2. Improved clarity: By giving meaningful names to your module aliases, you can make your code more self-explanatory and easier to follow.
  3. Easy refactoring: If you decide to change the path or name of a module, you only need to update the alias declaration, rather than every instance of the import statement.

Overall, module aliases are a powerful feature in TypeScript that can greatly enhance the readability and maintainability of your code.

Best Practices for Organizing TypeScript Modules

TypeScript modules allow developers to organize their code into reusable and easily maintainable units. However, as the codebase grows, it becomes important to follow certain best practices to ensure the modules are organized effectively. Here are some best practices for organizing TypeScript modules.

1. Use a Clear and Consistent Folder Structure

  • Organize your modules into logical groupings based on functionality or domain.
  • Use clear and descriptive folder names to indicate the purpose of each module.
  • Avoid creating a deep folder structure. Keep the structure as flat as possible for easier navigation.

2. Separate Concerns with a Barrel File

  • Create a barrel file (index.ts) in each folder to export the public API of that module.
  • This helps in decoupling the internal implementation details of the module from its consumers.
  • Export only the necessary components, classes, functions, or interfaces from the barrel file.

3. Use Explicit Imports

  • Avoid using wildcard imports (e.g., import * as moduleName from ‘./moduleName’) as they can lead to unwanted dependencies and increase the bundle size.
  • Use explicit imports (e.g., import { functionName } from ‘./moduleName’) to only import the specific entities needed.
  • This helps in reducing the chance of importing unnecessary code and improves tree shaking during the build process.

4. Minimize Exported Members

  • Only export the entities that are intended to be used by other modules.
  • Do not export internal implementation details or private members.
  • This helps in maintaining encapsulation and improves code maintainability.

5. Apply Liskov Substitution Principle

  • Follow the Liskov Substitution Principle when designing module dependencies.
  • Ensure that the modules can be used interchangeably without affecting the correctness of the program.
  • Avoid creating tight coupling between modules that can lead to maintenance issues in the long run.

6. Document Module Dependencies

  • Document the dependencies between modules using comments or documentation tools.
  • Clearly mention the required version or compatibility constraints, if any.
  • This helps in understanding the dependencies and potential impacts when making changes to a module.

7. Regularly Review and Refactor

  • Regularly review the organization and structure of your TypeScript modules.
  • Refactor and reorganize modules as needed to improve maintainability and readability.
  • Consider the evolving requirements of your application and adapt the module organization accordingly.

By following these best practices, you can create a well-organized TypeScript module structure that is easy to navigate, test, and maintain.

FAQ:

What is a module in TypeScript?

A module in TypeScript is a way to organize and separate code into smaller reusable pieces. It helps to keep the codebase clean, maintainable, and scalable.

How can I create a module in TypeScript?

In TypeScript, you can create a module by using the `export` keyword in front of functions, classes, or variables that you want to make accessible outside of the module. These exported members can then be imported and used by other modules.

What is the difference between `export` and `export default`?

The `export` keyword is used to export specific functions, classes, or variables from a module. On the other hand, the `export default` keyword is used to export a single default function, class, or variable that can be imported with any name.

How can I import a module in TypeScript?

In TypeScript, you can import a module using the `import` keyword followed by the name of the module you want to import. You can also specify the specific member you want to import by using the `import { memberName } from ‘moduleName’` syntax.

Can I use an alias for imported module names?

Yes, you can use an alias for imported module names by using the `as` keyword. For example, you can write `import { memberName as alias } from ‘moduleName’` to import a member with an alias.

What happens if I try to import a module that doesn’t exist?

If you try to import a module that doesn’t exist, TypeScript will throw a compilation error stating that the module could not be found. It’s important to make sure that the module you are trying to import exists and is properly installed before importing it.

Can I export and import modules in a nested structure?

Yes, you can export and import modules in a nested structure. TypeScript supports multi-level module nesting, allowing you to create a hierarchical structure of modules for better organization and encapsulation of code.