Skip to content

TypeScript - Ambients

TypeScript’s power comes from its static type system. However, much of the JavaScript ecosystem consists of existing libraries written without TypeScript types. Declaration files (with a .d.ts extension) bridge this gap.

A declaration file describes the shape (types, function signatures, classes, variables) of existing JavaScript code for the TypeScript compiler. It tells TypeScript what types to expect, enabling type checking, autocompletion, and refactoring support when using JavaScript libraries within a TypeScript project.

Declaration files contain only type information; they have no implementation code and are not compiled into JavaScript output. They are purely for the benefit of the developer and the compiler during development.

There are several ways to get declaration files for JavaScript libraries:

  1. Bundled with the Library: Many modern JavaScript libraries written in TypeScript or actively maintained now include their own .d.ts files directly within their npm package. When you install such a library, TypeScript automatically finds and uses these types.
  2. DefinitelyTyped (@types): This is a massive community-driven repository containing declaration files for thousands of popular JavaScript libraries. You can install types for a library (e.g., lodash, react, node) from npm under the @types scope. For example: npm install --save-dev @types/lodash. The TypeScript compiler automatically looks for types in node_modules/@types.
  3. Writing Your Own: If types are not available from the library or DefinitelyTyped, you can write your own .d.ts file to describe the parts of the library you use. This is often necessary for internal JavaScript code or less common libraries.

Using Declaration Files: Example with @types

Section titled “Using Declaration Files: Example with @types”

Let’s use the popular lodash library as an example. First, install the library and its types:

npm install lodash
npm install --save-dev @types/lodash

Now, you can import and use lodash functions in your TypeScript code with full type safety and autocompletion:

main.ts
import _ from 'lodash';
const numbers = [1, 5, 8, 10, 1, 5];
const uniqueNumbers = _.uniq(numbers);
console.log(uniqueNumbers); // Output: [ 1, 5, 8, 10 ]
const message = "hello world";
const capitalizedMessage = _.capitalize(message);
console.log(capitalizedMessage); // Output: Hello world
// Try using a function incorrectly - TypeScript will catch it:
// const result = _.uniq("not an array"); // Error: Argument of type 'string' is not assignable to parameter of type 'List<any> | null | undefined'.

Because we installed @types/lodash, TypeScript understands the signatures of _.uniq, _.capitalize, and other lodash functions.

When you need to describe JavaScript code that doesn’t have readily available types, you use the declare keyword within a .d.ts file (or sometimes directly in a .ts file for simple cases).

Imagine a simple JavaScript library my-js-lib.js:

// my-js-lib.js (pretend this exists)
function greetLib(name) {
return `Hello from lib, ${name}!`;
}
const LIBRARY_VERSION = '1.0.0';

You could create a corresponding my-js-lib.d.ts file:

// my-js-lib.d.ts
// Declare a function that exists in the JavaScript file
declare function greetLib(name: string): string;
// Declare a constant variable that exists
declare const LIBRARY_VERSION: string;
// You might also declare modules, classes, interfaces etc.
declare module 'some-other-module' {
export function processData(data: any): void;
}

Now, in your TypeScript code, assuming the JavaScript file is loaded somehow (e.g., via a <script> tag or a bundler), you can use these declared elements with type checking:

// app.ts
/// <reference path="my-js-lib.d.ts" />
// (Triple-slash reference might be needed if not using module system or tsconfig includes)
const greeting = greetLib("TypeScript User");
console.log(greeting);
console.log(`Library version: ${LIBRARY_VERSION}`);
// const wrongGreeting = greetLib(123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.

The declare keyword signals to TypeScript that the implementation exists elsewhere and it should trust the provided type signature.

  • Purpose: Provide type information for existing JavaScript code.
  • Extension: .d.ts
  • Content: Type declarations only (interfaces, types, function signatures, declare var/let/const/function/class/module...), no implementation.
  • Runtime: No impact on the generated JavaScript.
  • Acquisition: Bundled, DefinitelyTyped (@types), or manually written.
  • declare Keyword: Used within .d.ts files to describe ambient (existing) code.

Understanding and utilizing declaration files is crucial for effectively integrating TypeScript into projects that rely on JavaScript libraries, ensuring type safety across your entire codebase.