0

我的 TypeScript 项目是模块化的,并且有几个配置文件。我为配置文件选择了TOML,因为它是一种非常直观的语言。

另外,我有一个main.toml可以启用/禁用模块的地方。我的配置类看起来像这样。它是为从中创建多个自动解析的配置而设计的。

import { parse } from 'toml';
import { readFileSync } from 'fs';
import { join } from 'path';

export class Config {
    private readonly _name: string;
    private readonly _path: string;
    private _config: object;

    constructor(name: string) {
        this._name = name;
        this._path = join(__dirname, '..', '..', 'config', `${name}.toml`);
        this._config = this.load();
    }

    public load(): object {
        let config: object = {};
        try {
            config = parse(readFileSync(this._path).toString());
        } catch (error) {
            if (error.code === 'ENOENT') {
                throw new Error(`config ${this._name}.toml was not found!`);
            } else {
                throw new Error(error);
            }
        }
        return config;
    }

    get config(): object {
        return this._config;
    }
}

这是我的主文件在我想要使用main.toml激活其他模块的位置的样子:

import { Config } from './init/config';

let config: object = {};
try {
    config = new Config('main').config;
} catch (error) {
    console.log(error.stack);
    process.exit(1);
}

for (const key in config.modules) {
    if (Object.prototype.hasOwnProperty.call(config.modules, key) && config.modules[key]) {
        require(`./modules/${key}/${key}`);
    } else {
        zoya.info(`skipping module ${key}...`);
    }
}

现在我遇到的问题是打字稿编译器每次使用时都会给我以下错误config.modules

TS2339: Property 'modules' does not exist on type 'object'.

顺便说一句,我可以压制它,@ts-ignore但我认为这是一些不好的做法,我想知道我是否能以某种方式阻止这种情况。

我还尝试了其他类似的 TOML 解析器,我希望会有所作为,但我遇到了完全相同的问题。

4

1 回答 1

1

Typescript 无法推断您解析的配置的结构是什么。请记住,Typescript 在编译时存在,但在运行时不存在,您解析的配置对象在运行时存在,但在编译时不存在。

你告诉 Typescript 你解析的配置是 type object,但object没​​有modules属性。

您在这里有两个选择:

  1. 定义_configany而不是object。这将告诉 Typescript 该对象可以是任何类型,这意味着它基本上不会被类型检查。
  2. 为你的配置对象定义接口,所以 Typescript 知道它应该从它们那里得到什么类型。琐碎:
interface ConfigDef {
   modules: SomeType[]
}

let config: ConfigDef = { modules: [] };
try {
    config = new Config('main').config as ConfigDef;
} catch (error) {
    console.log(error.stack);
    process.exit(1);
}

或者,更严格地说,使用泛型(这可能更好):

export class Config<T> {
    private readonly _name: string;
    private readonly _path: string;
    private _config: T;

    constructor(name: string) {
        this._name = name;
        this._path = join(__dirname, '..', '..', 'config', `${name}.toml`);
        this._config = this.load();
    }

    public load(): T {
        let config: T = {};
        try {
            config = parse(readFileSync(this._path).toString()) as T;
        } catch (error) {
            if (error.code === 'ENOENT') {
                throw new Error(`config ${this._name}.toml was not found!`);
            } else {
                throw new Error(error);
            }
        }
        return config;
    }

    get config(): T {
        return this._config;
    }
}

// ...

let config: ConfigDef = { modules: [] };
try {
    config = new Config<ConfigDef>('main').config;
} catch (error) {
    console.log(error.stack);
    process.exit(1);
}

于 2020-08-27T19:42:58.617 回答