I'm trying to add types for the config
module specific to our app. The config
module is dynamically generated from a JSON file so it's tricky to type. Since it's a node module, I'm using 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;
}
How do I also export AppConfig
so I can use it as a type like so:
import * as config from 'config';
const appConfig: config.AppConfig;
If I export AppConfig directly in the config
module it errors with:
TS2309: An export assignment cannot be used in a module with other exported elements.
If I move AppConfig
to another file (e.g. ./app_config
) to hold the exports and import them into config.d.ts
it errors with:
TS2439: Import or export declaration in an ambient module declaration cannot reference module through relative module name.
If I put the AppConfig
export in the same file, but outside the config
module, it errors with:
TS2665: Invalid module name in augmentation. Module 'config' resolves to an untyped module at $PROJ/config/lib/config.js, which cannot be augmented.
This is similar to Typescript error "An export assignment cannot be used in a module with other exported elements." while extending typescript definitions with the requirement that I want to be able to import AppConfig
as a type directly in other TS files.
The answer requires a confusing Typescript concept:
Declaration merging - the compiler merges two separate declarations declared with the same name into a single definition. In this case, we create 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 use the imports like so:
import * as config from 'config';
import { FakeEnum, MyInterface } from 'config';
As a side note, you cannot use enums
with an ambient module (the declare module 'config'
) because enums compile to a JS object and you can't add new object to a module you don't control. You can work around the issue by faking an enum with a union type:
type FakeEnum = 'A' | 'B' | 'C';
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With