What are Ambients in TypeScripts and when to use them?


TypeScript, a superset of JavaScript, brings static typing to the JavaScript ecosystem. It enables developers to catch errors early and write more robust code. One of the powerful features TypeScript offers is the ability to define and enforce types for variables, functions, classes, and more. However, there are scenarios where TypeScript lacks type information for external JavaScript libraries or modules. To bridge this gap and enable proper type checking, TypeScript provides a feature called "Ambients."

In this tutorial, we will explore what ambients are, how to use them, and when they are beneficial.

What are Ambients?

Ambients in TypeScript provide a way to describe the shape of variables, functions, objects, or modules that exist outside the TypeScript ecosystem. They allow developers to define type information for code that is written in JavaScript or for external libraries that don't have type definitions. Ambients act as declarations that provide type annotations and enable the TypeScript compiler to understand and validate the usage of external code.

Ambients Serve two Primary Purposes

  • To provide type information for existing JavaScript codebases: TypeScript allows developers to gradually introduce type checking into JavaScript projects. By using ambients, type information can be added incrementally to parts of the codebase without rewriting everything in TypeScript.

  • To provide type declarations for external libraries: TypeScript can leverage ambients to declare the structure and types of objects, functions, or classes provided by external JavaScript libraries that lack official TypeScript definitions. This empowers developers to enjoy the benefits of TypeScript's type-checking and auto-completion within their projects.

Using Ambients

Ambients are defined using ambient declarations, which are declarations without an implementation. The ambient keyword tells the TypeScript compiler that the declaration exists elsewhere, typically in a JavaScript file or an external library.

Syntax

To create an ambient declaration, use the declare keyword followed by the entity you want to declare. Here's the general syntax for ambient declarations −

declare <entity>

The <entity> can be a variable, function, class, interface, or module. Let's explore a few examples to illustrate how ambients work.

Example 1

Let's consider a practical example.

Let’s create a module named my-library which has a utility function capitalize, that takes a string and returns the capitalized version of it.

my-library.js

function capitalize(st) {
   return st.charAt(0).toUpperCase() + st.slice(1);
}
module.exports = {
   capitalize: capitalize,
};

Now, we will create a type definition file to restrict the usage of the exported capitalize function with compile-time type checking −

First, create a new file named my-library.d.ts with the following content −

declare module 'my-library' {
   export function capitalize(str: string): string;
}

In the example above, we declare a module named "my-library" and export a function capitalize that takes a string parameter and returns a string.

You can now use the capitalize function in your TypeScript code with type safety. We create an app.ts file now to use our capitalize function −

app.ts

/// <reference types="node" />
const { capitalize } = require("./my-library");
const result = capitalize("hello");
console.log(result); // Output: Hello

By adding the /// <reference types="node" /> directive, you inform TypeScript to use the installed Node.js type definitions for the require function. Now, TypeScript should recognize the require function.

By providing accurate type definitions, the TypeScript compiler can validate the usage of the capitalize function, preventing any potential type errors.

Output

Hello

Example 2

For example, suppose you're using a JavaScript library called "moment.js" for date manipulation in your TypeScript project. By defining an Ambient declaration for "moment.js," you can ensure that the TypeScript compiler understands the library's API and provides type-checking support −

moment.d.ts −

declare module "moment" {
   function moment(): moment.Moment;
   function add(number: number, unit: string): moment.Moment;
   // Add other function declarations if needed
}
declare namespace moment {
   interface Moment {
      format(format: string): string;
      // Add other method declarations if needed
   }
}

test1.ts

import moment from "moment";

const now = moment();
const tomorrow = moment().add(1, "day");

console.log(now.format("YYYY-MM-DD")); // Output: Current date in YYYY-MM-DD format
console.log(tomorrow.format("YYYY-MM-DD")); // Output: Tomorrow's date in YYYY-MM-DD format

Output

2023-05-26
2023-05-27

Here, I have used ts-node, an npm package that is used for JIT (just in time) compilation. You can install it using npm −

npm i -g ts-node

Benefits of Ambient

  • Type Safety − Ambients enable TypeScript to provide type safety even when working with external JavaScript libraries. The type declarations ensure that the usage of external code aligns with the expected types, reducing the chances of runtime errors.

  • Tooling Support − By using ambients, developers gain the advantage of enhanced tooling support, such as autocompletion, intelligent suggestions, and accurate error reporting in code editors. This significantly improves the developer experience and productivity.

  • Gradual Typing − Ambients allow developers to introduce static typing to existing JavaScript projects gradually. TypeScript can be incrementally adopted by providing type annotations for specific parts of the codebase, reducing the effort and risks associated with a full-scale rewrite.

Conclusion

Ambients in TypeScript provide a powerful mechanism for describing the shape and behavior of external JavaScript code. They allow developers to bridge the gap between TypeScript and existing JavaScript libraries, legacy codebases, or browser APIs that lack explicit TypeScript support. By creating ambient declarations, developers can harness the benefits of TypeScript's static typing, editor support, and refactoring tools.

In this article, we explored the concept of ambients in TypeScript and saw how to use them effectively. We learned that ambient declarations are created using the declare keyword, which informs the TypeScript compiler that the declaration describes an external entity.

Updated on: 21-Aug-2023

61 Views

Kickstart Your Career

Get certified by completing the course

Get Started
Advertisements