如何在同一命名空间中的多个文件上合并打字稿接口
How can I merge typescript interfaces over multiple files in the same namespace
我正在尝试重构一个笨重的配置 interface/object,方法是将它的各个部分分成单独的文件,放在 namespace
我巧妙地命名为 Config
.
下
文档中提到 namespaces that span multiple files and declaration merging of interfaces,但我似乎无法让它们协同工作。
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
/// <reference path="./index.ts" />
namespace Config {
export interface IConfig {
server: {
host: string;
port: number;
}
}
}
src/index.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;
我尝试了多种导出方式,例如 export default Config;
、export namespace Config {...}
在每个 src/config/...
文件中,将 export const config
更改为 export var config
.在 src/config/index.ts
中,我尝试了 export * from './server'
。似乎没有任何帮助。
我有一种感觉,我正在做这一切都是错误的。
奇怪的是,每个文件中命名空间内的接口都是从命名空间导出的,所以在src/index.ts
中,我可以这样做:
import IConfig = Config.IConfig;
let c: IConfig;
console.log(c.server.host);
但我无法在 src/config/index.ts
或 src/config/server.ts
中做到这一点。
首先你应该自己决定,如果你想将 config
对象分配给模块范围(即 import
/export
)或全局范围(即window
在浏览器中,global
在节点中)。
命名空间的主要目的是在全局范围内定义properties/values。正如您在链接中正确指出的那样,合并了同名命名空间 - 包括包含的内部成员,如 IConfig
接口。
这是一笔交易:只有当包含 namespace
的文件是脚本(顶级没有 import
/export
的非模块文件)时才会发生合并。
在 src/config/index.ts
中,您有 import
条语句,因此文件成为一个模块并且 namespace Config
不会被合并。相反,它是一个模块内部命名空间,甚至没有 export
ed(参见文档中的 Needless Namespacing, Do not use namespaces in modules)。 src/config/server.ts
中的 Config
命名空间形成了自己的全局命名空间(非模块文件),这就是为什么您仍然可以使用包含的 IConfig
类型。
总而言之,如果您想要全局配置(值和类型),请确保多文件部分命名空间的每个部分都在非模块文件中声明。如果要从模块导出配置(如果可行,首选方式!;更好的封装,没有全局范围污染,"modern" 方式),请继续阅读。
备选方案:在模块中导出配置
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:
import { ServerConfig } from "./server"
interface CommonConfig {
someGeneralProperty: {
// ...
}
}
export type IConfig = CommonConfig & ServerConfig
export const config: IConfig = readConfig(); // whatever readConfig looks like
src/index.ts:
import { config } from './config';
config.server;
config.someGeneralProperty
随意调整您需要的部分。希望对您有帮助。
我正在尝试重构一个笨重的配置 interface/object,方法是将它的各个部分分成单独的文件,放在 namespace
我巧妙地命名为 Config
.
文档中提到 namespaces that span multiple files and declaration merging of interfaces,但我似乎无法让它们协同工作。
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
/// <reference path="./index.ts" />
namespace Config {
export interface IConfig {
server: {
host: string;
port: number;
}
}
}
src/index.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;
我尝试了多种导出方式,例如 export default Config;
、export namespace Config {...}
在每个 src/config/...
文件中,将 export const config
更改为 export var config
.在 src/config/index.ts
中,我尝试了 export * from './server'
。似乎没有任何帮助。
我有一种感觉,我正在做这一切都是错误的。
奇怪的是,每个文件中命名空间内的接口都是从命名空间导出的,所以在src/index.ts
中,我可以这样做:
import IConfig = Config.IConfig;
let c: IConfig;
console.log(c.server.host);
但我无法在 src/config/index.ts
或 src/config/server.ts
中做到这一点。
首先你应该自己决定,如果你想将 config
对象分配给模块范围(即 import
/export
)或全局范围(即window
在浏览器中,global
在节点中)。
命名空间的主要目的是在全局范围内定义properties/values。正如您在链接中正确指出的那样,合并了同名命名空间 - 包括包含的内部成员,如 IConfig
接口。
这是一笔交易:只有当包含 namespace
的文件是脚本(顶级没有 import
/export
的非模块文件)时才会发生合并。
在 src/config/index.ts
中,您有 import
条语句,因此文件成为一个模块并且 namespace Config
不会被合并。相反,它是一个模块内部命名空间,甚至没有 export
ed(参见文档中的 Needless Namespacing, Do not use namespaces in modules)。 src/config/server.ts
中的 Config
命名空间形成了自己的全局命名空间(非模块文件),这就是为什么您仍然可以使用包含的 IConfig
类型。
总而言之,如果您想要全局配置(值和类型),请确保多文件部分命名空间的每个部分都在非模块文件中声明。如果要从模块导出配置(如果可行,首选方式!;更好的封装,没有全局范围污染,"modern" 方式),请继续阅读。
备选方案:在模块中导出配置
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:
import { ServerConfig } from "./server"
interface CommonConfig {
someGeneralProperty: {
// ...
}
}
export type IConfig = CommonConfig & ServerConfig
export const config: IConfig = readConfig(); // whatever readConfig looks like
src/index.ts:
import { config } from './config';
config.server;
config.someGeneralProperty
随意调整您需要的部分。希望对您有帮助。