在 TypeScript 环境模块中导出赋值和导出类型

Joe*_*Joe 1 typescript typescript-typings

我正在尝试为config特定于我们的应用程序的模块添加类型。该config模块是从 JSON 文件动态生成的,因此很难输入。因为它是一个节点模块,所以我使用环境模块进行打字。

// config.d.ts
declare module 'config' {
  interface AppConfig {
    name: string;
    app_specific_thing: string;
  }
  const config: AppConfig;
  export = config;
}
Run Code Online (Sandbox Code Playgroud)

我如何还导出AppConfig以便我可以将它用作这样的类型:

import * as config from 'config';

const appConfig: config.AppConfig;
Run Code Online (Sandbox Code Playgroud)

尝试

  • 如果我直接在config模块中导出 AppConfig,它会出错:

    TS2309:不能在具有其他导出元素的模块中使用导出分配。

  • 如果我移动AppConfig到另一个文件(例如./app_config)来保存导出并将它们导入到config.d.ts它的错误中:

    TS2439:环境模块声明中的导入或导出声明不能通过相对模块名称引用模块。

  • 如果我将AppConfig导出放在同一个文件中,但在config模块之外,则会出现以下错误:

    TS2665:扩充中的模块名称无效。模块 'config' 解析为 $PROJ/config/lib/config.js 中的无类型模块,无法扩充。

这类似于Typescript 错误“不能在具有其他导出元素的模块中使用导出分配”。同时根据我希望能够AppConfig直接在其他 TS 文件中作为类型导入的要求扩展打字稿定义

Joe*_*Joe 6

答案需要一个令人困惑的 Typescript 概念:

声明合并- 编译器将使用相同名称声明的两个单独声明合并为一个定义。在这种情况下,我们创建了两个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;
}
Run Code Online (Sandbox Code Playgroud)

您可以像这样使用导入:

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

Run Code Online (Sandbox Code Playgroud)

附带说明一下,您不能enums环境模块(the declare module 'config')一起使用,因为枚举会编译为 JS 对象,并且您无法向不受控制的模块添加新对象。您可以通过伪造具有联合类型的枚举来解决此问题:

type FakeEnum = 'A' | 'B' | 'C';
Run Code Online (Sandbox Code Playgroud)