Hone logo
Hone
Problems

Mastering Ambient Declarations in TypeScript

TypeScript's strength lies in its ability to provide static typing to JavaScript code. Sometimes, you need to use existing JavaScript libraries that don't have TypeScript definitions or declare global variables/functions that are not managed by a module system. This is where ambient declarations shine. This challenge will test your understanding and ability to create ambient declarations for such scenarios.

Problem Description

Your task is to create ambient declaration files (.d.ts) for a hypothetical, globally available JavaScript library named "LegacyLib". This library exposes a single global object LegacyLib with a version property (a string) and a greet function that takes a name (string) and returns a greeting string. You also need to declare a global function consoleLog that simply logs a message to the console.

You will then write a small TypeScript program that utilizes these ambient declarations to interact with "LegacyLib" and the consoleLog function.

Key Requirements:

  1. Declare LegacyLib:
    • It should be a global object.
    • It must have a version property of type string.
    • It must have a greet method that accepts one argument, name of type string, and returns a string.
  2. Declare consoleLog:
    • It should be a global function.
    • It must accept one argument, message of type string.
    • It should return void.
  3. Implement a TypeScript program:
    • This program should access LegacyLib.version.
    • It should call LegacyLib.greet("World").
    • It should call consoleLog("Hello from TypeScript!").

Expected Behavior:

When the TypeScript program is compiled and the generated JavaScript is executed in an environment where LegacyLib and consoleLog are present globally (e.g., in a browser's <script> tags or a Node.js environment with these globally injected), it should:

  • Access the version property of LegacyLib.
  • Successfully call the greet method.
  • Successfully call the consoleLog function.

Examples

Assume the following JavaScript code is present in the environment:

legacy-lib.js (simulated global environment)

var LegacyLib = {
  version: "1.0.0",
  greet: function(name) {
    return "Hello, " + name + "!";
  }
};

function consoleLog(message) {
  console.log(message);
}

Example 1: Basic Usage

Input: A TypeScript file (app.ts) containing the following code:

// Assume legacy-lib.js and its global functions are available in the runtime environment.
// Your ambient declarations will go into a separate .d.ts file.

const libVersion = LegacyLib.version;
const greeting = LegacyLib.greet("User");
consoleLog(`Library Version: ${libVersion}`);
consoleLog(`Greeting: ${greeting}`);

Output (from compilation and execution):

Library Version: 1.0.0
Greeting: Hello, User!

Explanation: The TypeScript compiler, using your ambient declarations, understands the structure of LegacyLib and consoleLog. The program can then access these global entities and their members as if they were statically typed.

Example 2: Type Safety Demonstration

Input: A TypeScript file (app.ts) containing:

// Assume legacy-lib.js and its global functions are available in the runtime environment.
// Your ambient declarations will go into a separate .d.ts file.

// This should cause a TypeScript compilation error because greet expects a string.
// const invalidGreeting = LegacyLib.greet(123);

// This should cause a TypeScript compilation error because version is a string, not a number.
// const versionAsNumber: number = LegacyLib.version;

consoleLog("Type safety checks are commented out but demonstrate intent.");

Output (during TypeScript compilation): TypeScript compiler would report errors for the uncommented lines:

Argument of type 'number' is not assignable to parameter of type 'string'.
Type 'string' is not assignable to type 'number'.

Explanation: The ambient declarations enforce type safety. Attempting to pass an incorrect type to LegacyLib.greet or assign LegacyLib.version to a variable of the wrong type will be caught at compile time by the TypeScript compiler.

Constraints

  • All declarations must be made using ambient module syntax or global declarations within .d.ts files.
  • The solution must consist of at least one .d.ts file containing the ambient declarations and one .ts file for the program logic.
  • The TypeScript program should not directly import anything. It should rely solely on global scope access.
  • Performance is not a primary concern for this challenge, but the declarations should be syntactically correct and follow TypeScript best practices.

Notes

  • Ambient declarations tell the TypeScript compiler about the shape of existing JavaScript code. They don't provide any actual implementation.
  • You can use declare global { ... } to declare global variables and functions, or declare const ... / declare var ... / declare function ... at the top level of a .d.ts file for global declarations.
  • Consider how you would structure your .d.ts file(s) to be easily included in a project. For instance, a single file named legacy-lib.d.ts is a common convention.
  • When you compile your TypeScript code, ensure that your .d.ts files are included in the compilation process. In a typical tsconfig.json, *.d.ts files are automatically included unless explicitly excluded.
Loading editor...
typescript