1

This is a follow up to How to use node-config in typescript? since I couldn't find a proper answer.

I'm relatively new to TS and I'm trying to convert our configs from json to typescript, while trying to incorporate types on the config.

What we currently have is something like:

// default.json
{
  "server": {
    "timeout": 10
  }
}

// some-code.ts
import config from 'config';
...
const serverTimeout = config.get<number>('server.timeout'); // 10

Here, we are able to get required configs, but we need to specify the type via generics in code. I'm trying to incorporate (relatively) stronger typing.

Dry conversion:

// default.ts
import { MyConfig } from './config-schema.ts'

const conf: MyConfig = {
  "server": {
    "timeout": 10
  }
};

export default conf;

// config-schema.ts
export interface MyConfig {
  server: {
    timeout: number;
  }
}

// some-code.ts
import config from 'config';

console.log(config); // { "server": { "timeout": 10 } }
console.log(config.get<number>('server.timeout')); // 10
console.log((config as any).server.timeout); // 10
console.log(config.server.timeout); // obviously a compile time error.

As you can see, the last form will NOT work and results in compile time ERROR. I somehow want config to conform to the shape of MyConfig so that config.server.timeout is possible with strong type number.

I need to somehow extend the IConfig interface with MyConfig or force a re-typing of config to IConfig & MyConfig.

My attempt:

// config-schema.ts
import config from 'config';

interface MyConfig {
  server: {
    timeout: number;
  }
}

export (config as any as MyConfig);

// some-code.ts
import config from './config-schema';

console.log(config.server.timeout); // 10

This works, but somehow doesn't feel right to me.

Problems

  • Forced to use any... can this be avoided somehow ?
  • Need to do something like import config from '../../core/config-schema'; -> looks ugly, path different for different files, need all project collaborators to follow convention... is it possible to keep it as import config from 'config'; and have the magic abstracted out ?
  • Compile default.ts to default.js with tscat compile time (so that typescript is not a runtime dependency). Have updated env var NODE_CONFIG_DIR accordingly. Currently I do this, but the config object gets created with an extra hierarchy level. i.e. instead of config.server.timeout, it became config.default.server.timeout

Edit: Third question resolved -- see comment

Codi
  • 511
  • 3
  • 19
  • 1
    For secondary part: I was able to do static compilation and set typescript as a devDependency. At app start, I set `process.env.NODE_CONFIG_DIR = __dirname + '/dist/config/';` for reading configs from the right location. Note: since config library now sees JS files, it expects syntax for JS config files, i.e. ends with `module.exports = configObject`. Hence in the TS file we need to do `export = configObject` rather than `export default configObject` since the latter produces JS equivalent to `exports.default = configObject` and that adds an extra `default` hierarchy level in the built config – Codi Mar 10 '20 at 06:24
  • Experiment using `console.log(JSON.stringify(config))` in code to see stuff – Codi Mar 10 '20 at 06:31

0 Answers0