Exporting declarations and different export types within a TypeScript ambient module

I am currently working on adding specific types for the config module in our application. The config module is generated dynamically from a JSON file, making it challenging to type. Since it is a node module, I am utilizing an ambient module for the typings.

// config.d.ts
declare module 'config' {
  interface AppConfig {
    name: string;
    app_specific_thing: string;
  }
  const config: AppConfig;
  export = config;
}

My goal is to also export AppConfig so that I can use it as a type like this:

import * as config from 'config';

const appConfig: config.AppConfig;

My Attempts

  • When attempting to export AppConfig directly within the config module, I encountered this error:

    TS2309: An export assignment cannot be used in a module with other exported elements.

  • Moving AppConfig to another file (e.g. ./app_config) and importing it into config.d.ts resulted in this error:

    TS2439: Import or export declaration in an ambient module declaration cannot reference module through relative module name.

  • Placing the AppConfig export outside of the config module in the same file led to this error:

    TS2665: Invalid module name in augmentation. Module 'config' resolves to an untyped module at $PROJ/config/lib/config.js, which cannot be augmented.

This issue resembles a situation discussed in this Stackoverflow thread, where the main requirement is to have the ability to import AppConfig as a type directly in other TypeScript files.

Answer №1

If you're feeling lost in the world of Typescript, one concept that may add to the confusion is declaration merging.

Declaration merging involves the compiler combining two separate declarations with the same name into a single definition. In this example, we have two declarations of config.

// config.d.ts
declare module 'config' {

  // This nested namespace 'config' will merge with the enclosing 
  // declared namespace 'config'.
  // https://www.typescriptlang.org/docs/handbook/declaration-merging.html
  namespace config {
    interface AppConfig {
      name: string;
      app_specific_thing: string;
      my_enum: FakeEnum;
    }

    interface MyInterface {}

    // See side note below
    type FakeEnum = 'A' | 'B' | 'C';
  }

  const config: AppConfig;
  export = config;
}

You can import and use these declarations like this:

import * as config from 'config';
import { FakeEnum, MyInterface } from 'config';

It's worth noting that you cannot use enums with an ambient module (like declare module 'config') because enums compile to a JS object which cannot be added to a module you don't control. To get around this limitation, you can mimic an enum using a union type:

type FakeEnum = 'A' | 'B' | 'C';

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The options passed to createReadStream in TypeScript do not accept {start: 90, end: 99}

After updating to TypeScript 1.6.2, I encountered an issue with my call to createReadStream(). The problem arises because the type definition in node.d.ts does not recognize 'start' and 'end' in the options parameter. var st = fs.crea ...

Using Typescript in a definition file requires classes and interfaces to be included in the compiled .js file

I am currently working on a Typescript project that consists of two crucial files: app.ts models.d.ts The initial lines of code in app.ts are as follows: ///<reference path="models.d.ts"/> 'use strict'; import * as fs from 'async-f ...

Using a Typescript typeguard to validate function parameters of type any[]

Is it logical to use this type of typeguard check in a function like the following: Foo(value: any[]) { if (value instanceof Array) { Console.log('having an array') } } Given that the parameter is defined as an array o ...

Bring in a collection of classes of various types from one TypeScript file to another

In my code file exampleA.ts, I define an object as follows: import { ExampleClass } from 'example.ts'; export const dynamicImportations = { ExampleClass }; Later, in another file named exampleB.ts, I import an array that includes class types and ...

Getting a JSON value and saving it to a variable in Angular 4

Here is the JSON structure: { "Semester": [ { "queueName": "Science", "totalCount": 300, "unassignedCount": 10, "subjectDetails": [ { "subjectName": "Chemistry", "sectionOne": 100, "secti ...

Exporting ExpressJS from a TypeScript wrapper in NodeJS

I've developed a custom ExpressJS wrapper on a private npm repository and I'm looking to export both my library and ExpressJS itself. Here's an example: index.ts export { myExpress } from './my-express'; // my custom express wrap ...

Encapsulating functions with multiple definitions in Typescript

Struggling with wrapping a function that can have multiple return types based on input parameters in Typescript. Imagine wanting a function to return ReturnA for VariantEnum.a and ReturnB for VariantEnum.b. Consider this implementation of sampleFunction: ...

Error: The token 'export' in d3zoom is causing a syntax issue

I'm encountering issues with executing tests in Angular: ({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){export {default as zoom} from "./zoom.js"; ...

Find all Mondays occurring within a specified date range using Moment.js

I need to extract all Mondays within a specific date range. let start = moment(this.absence.FromDate); let end = moment(this.absence.ToDate); The user has the option to deactivate certain weekdays during this period by setting booleans. monday = true; t ...

Angular 5's data display glitch

Whenever I scroll down a page with a large amount of data, there is a delay in rendering the data into HTML which results in a white screen for a few seconds. Is there a solution to fix this issue? Link to issue I am experiencing HTML binding code snippe ...

Dynamic Object properties are not included in type inference for Object.fromEntries()

Hey there, I've been experimenting with dynamically generating styles using material UI's makeStyles(). However, I've run into an issue where the type isn't being correctly inferred when I use Object.fromEntries. import * as React from ...

Tips for accessing an item from a separate TypeScript document (knockout.js)

In the scenario where I need to utilize an object from another TypeScript file, specifically when I have an API response in one.ts that I want to use in two.ts. I attempted exporting and importing components but encountered difficulties. This code snippe ...

Trigger functions on a universal component from the nested component

I am currently working on an Angular project with two components, one being a generic component where the table is defined and the other being a component that invokes this table by passing the required data. However, I encountered an issue where the tabl ...

Group data by two fields with distinct values in MongoDB

I have developed a Typescript Node.js application and I am looking to organize documents by two fields, "one_id" and "two_id", based on a specific "one_id" value. Below is the data within my collection: { "_id":"5a8b2953007a1922f00124fd", "one_id ...

Encountering a problem with the Material UI Autocomplete component when trying to implement

I am trying to integrate a Material UI autocomplete component with a checkbox component. Is there a way to have the checkbox get checked only when a user selects an option from the autocomplete? You can check out my component through the following links: ...

Using a single Material Autocomplete input to handle two values within Angular

Looking to implement a search feature using Material's autocomplete that can filter by either user name or user ID. The current implementation is partially functional in this Stackblitz. When selecting a user name from the autocomplete options with a ...

What is the step-by-step process for implementing tooltips in Ant Design Menu after version 4.20.0?

According to the Ant Design documentation: Starting from version 4.20.0, a simpler usage <Menu items={[...]} /> is provided with enhanced performance and the ability to write cleaner code in your applications. The old usage will be deprecated in th ...

What steps can I take to ensure my dynamic route functions correctly in NextJs?

// /home/[storeId]/layout.tsx import prismadb from "@/lib/prismadb"; import { auth } from "@clerk/nextjs/server"; import { redirect } from "next/navigation"; export default async function DashboardLayout({ children, params, ...

The POST requests on Next JS Mock API endpoints include parameters passed in the req.body

I am currently running Next JS API tests using jest with a custom testClient. The code for the testClient is as follows: import { createServer } from 'http'; import type { NextApiHandler } from 'next'; import type { __ApiPreviewProps } ...

Utilize a combination of generic parameters as the keys for objects in TypeScript

Is there a method to utilize multiple generic parameters as object keys in TypeScript? I came across this answer which works well when there is only one parameter, but encounters issues with more than one. The error message "A mapped type may not declar ...