Hur kan jag slå samman TypeScript-gränssnitt över flera filer i samma namnrymd


Jag försöker omstrukturera ett otympligt konfigurationsgränssnitt/-objekt genom att separera dess olika sektioner i separata filer under ett namespace som jag listigt har döpt till Config.

Dokumentationen talar om namnrymder som sträcker sig över flera filer och deklarationssammanfogning av gränssnitt, men jag verkar inte kunna få dem att fungera tillsammans.

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;

Jag har försökt flera olika varianter av att exportera saker, såsom export default Config;, export namespace Config {...} i varje src/config/...-fil, ändrat export const config till export var config. I src/config/index.ts har jag försökt export * from './server'. Ingenting verkar hjälpa.

Jag har en känsla av att jag helt enkelt gör allt det här på fel sätt.

Konstigt nog exporteras gränssnitten inom namnrymden i varje fil från namnrymden, så i src/index.ts kan jag göra:

import IConfig = Config.IConfig;

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

men jag kan inte göra det varken i src/config/index.ts eller src/config/server.ts.

Svar #1

Först bör du själv bestämma om du vill tilldela config-objektet till ett modultäckande område (d.v.s. import/export) eller i det globala området (d.v.s. window i webbläsare, global i node).

Huvudsyftet med namnrymder är att definiera egenskaper/värden på den globala nivån. Som du korrekt påpekade med länkarna, slås namnrymder med samma namn samman - det inkluderar innehållande inre medlemmar som IConfig-gränssnittet.

Här är avtalet: Sammanfogning sker endast när filen som innehåller namespace är ett skript (en icke-modulfil utan import/export på toppnivå).

I src/config/index.ts har du import-satser, så filen blir en modul och namespace Config slås inte ihop. Istället är det snarare ett modulintern namespace, som inte ens exporteras (se Needless Namespacing, Do not use namespaces in modules i dokumentationen). Config-namespace i src/config/server.ts bildar sitt eget globala namespace (icke-modulfil), det är därför du fortfarande kan använda den inneslutna IConfig-typen.

Sammanfattningsvis, om du vill ha konfigurationen (värde och typ) globalt, se till att varje del av namnområdet för flerfilssystemet deklareras i en icke-modulfil. Om konfigurationen ska exporteras från en modul (föredraget sätt om möjligt!; bättre inkapsling, ingen global scopesförorening, det "moderna" sättet), fortsätt läsa.

Alternativ: exportera konfiguration i en modul

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

Känn dig fri att justera de delar du behöver.

Relaterade artiklar