简体   繁体   English

如何在同一命名空间中的多个文件上合并打字稿接口

[英]How can I merge typescript interfaces over multiple files in the same namespace

I am trying to refactor an unwieldy config interface/object by separating its various sections into separate files under a namespace I've cleverly named Config .我试图重构一个笨拙的配置接口/对象,方法是将其各个部分分成我巧妙命名的namespace下的单独文件Config

The documentation talks about namespaces that span multiple files and declaration merging of interfaces , but I can't seem to get them to work together.该文档讨论了跨越多个文件的命名空间接口的声明合并,但我似乎无法让它们一起工作。

src/config/index.ts src/config/index.ts

/// <reference path="./server.ts" />
import fs from 'fs';
import json5 from 'json5';

const _config = readConfig();

namespace Config {
    export const config = _config;

    export interface IConfig {
        someGeneralProperty: {
            // ...
        }
    }
}

function readConfig(): Config.IConfig {
    return json5.parse(fs.readFileSync('./path/to/config.json', 'utf-8'));
}

function doSomeOtherStuff() {
    // fails: Property 'server' does not exist on type 'IConfig'.
    console.log(_config.server.host);
}

src/config/server.ts src/config/server.ts

/// <reference path="./index.ts" />

namespace Config {
    export interface IConfig {
        server: {
            host: string;
            port: number;
        }
    }
}

src/index.ts源代码/索引.ts

// fails: Module '"./config"' has no exported member 'config'.
import { config } from './config'; 

// fails: Cannot use namespace 'Config' as a value.
// fails: Namespace 'Config' has no exported member 'config'.
import config = Config.config;

I've tried several variations of exporting things, such as export default Config;我尝试了多种导出方式,例如export default Config; , export namespace Config {...} in each of the src/config/... files, changing export const config to export var config . , export namespace Config {...}在每个src/config/...文件中,将export const config更改为export var config In src/config/index.ts I tried export * from './server' .src/config/index.ts我尝试export * from './server' Nothing seems to help.似乎没有任何帮助。

I have a feeling I'm just going about this all wrong.我有一种感觉,我只是把这一切都错了。

Oddly, the interfaces within the namespace in every file are exported from the namespace, so in src/index.ts , I can do:奇怪的是,每个文件中命名空间内的接口都是从命名空间导出的,所以在src/index.ts ,我可以这样做:

import IConfig = Config.IConfig;

let c: IConfig;
console.log(c.server.host);

but I cannot do that in either src/config/index.ts nor src/config/server.ts .但我不能在src/config/index.tssrc/config/server.ts做到这一点。

At first you should decide yourself, if you want to assign the config object to a module scope (ie import / export ) or in the global scope (ie window in browsers, global in node).首先,你应该自己决定,如果你想指定config对象模块范围(即import / export ),或在全球范围内(即window在浏览器中, global的节点)。

The main purpose of namespaces is to define properties/values on the global scope.命名空间的主要目的是在全局范围内定义属性/值。 As you pointed out correctly with the links, equally named namespaces are merged - that includes contained inner members like the IConfig interface.正如您在链接中正确指出的那样,合并了同名命名空间 - 包括包含的内部成员,如IConfig接口。

Here is the deal: Merging only happens when the file containing the namespace is a script (a non-module file without import / export at top-level).这是交易:合并仅在包含namespace的文件是脚本(在顶级没有import / export的非模块文件)时发生。

In src/config/index.ts , you've got import statements, so the file becomes a module and namespace Config does not get merged.src/config/index.ts ,您有import语句,因此该文件成为一个模块并且namespace Config不会合并。 Instead it is rather a module internal namespace, which is not even export ed (see Needless Namespacing , Do not use namespaces in modules in the docs).相反,它是一个模块内部命名空间,它甚至不是export ed(参见Needless NamespacingDo not use namespaces in modules in the docs)。 The Config namespace in src/config/server.ts forms its own global namespace (non-module file), that is why you can still use the contained IConfig type. src/config/server.tsConfig命名空间形成了它自己的全局命名空间(非模块文件),这就是您仍然可以使用包含的IConfig类型的原因。

In summary, if you want to have the config (value and type) globally, make sure, every part of the multi file part namespace is declare in a non-module file.总之,如果您希望全局配置(值和类型),请确保多文件部分命名空间的每个部分都在非模块文件中声明。 If the config is to be exported from a module (preferred way if feasible!; better encapsulation, no global scope pollution, the "modern" way), read on.如果要从模块导出配置(如果可行,首选方式!;更好的封装,没有全局范围污染,“现代”方式),请继续阅读。

Alternative: export config in a module替代方法:在模块中导出配置

src/config/server.ts: src/config/server.ts:

export interface ServerConfig {
  server: {
    host: string;
    port: number;
  }
}

// you could also read a server-specific config value here, export it
// and merge it with a separately read common config value in index.ts
// export serverConfig: ServerConfig = readServerConfig()

src/config/index.ts: src/config/index.ts:

import { ServerConfig } from "./server"

interface CommonConfig {
  someGeneralProperty: {
    // ...
  }
}

export type IConfig = CommonConfig & ServerConfig

export const config: IConfig = readConfig(); // whatever readConfig looks like

src/index.ts:源代码/索引.ts:

import { config } from './config'; 

config.server;
config.someGeneralProperty

Feel free to adjust the parts, you need.随意调整零件,你需要。 Hope, it helps.希望能帮助到你。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM