In my opinion the biggest drawback of type safety is that it can create a false sense of security due to the common confusion between compile-time safety and runtime safety. It is especially true for node-config where the config is the product of merging multiple files and environment variables. This is why any solution that applies a type to your config without checking that it actually maps that type at runtime can create problems down the line. To solve this you could have a look at type guarding solutions like typia or zod for example.
Personally I use tools that are usually already present in my projects : JTD + Ajv. Here is a recipe if somebody is interested.
config
├── config.jtd.json
├── custom-environment-variables.js
├── default.json
└── development.json
src
├── config.ts
types
└── index.ts
The file config/config.jtd.json
is written by hand, it looks like this (optionalProperties is used to ignore some things added by node-config, merging the interface with IConfig provided by node-config might be a better way to do the same thing):
{
"properties": {
"port": { "type": "uint32"}
},
"optionalProperties": { "util": {}, "get": {}, "has": {} }
}
The file types/index.ts
contains a Config
interface, it was created using jtd-codegen:
jtd-codegen config/config.jtd.json --typescript-out types
Then src/config.ts
performs the validation and type casting:
import fs from 'fs'
import config from 'config'
import Ajv from 'ajv/dist/jtd'
import { type Config } from '../types'
const validate = new Ajv().compile(JSON.parse(fs.readFileSync('config/config.jtd.json', 'utf8')))
if (!validate(config)) throw new Error('invalid config', { cause: validate.errors })
config.util.makeImmutable(config)
const typedConfig = config as unknown as Config
export default typedConfig
Now when importing src/config.ts
you have both compile-time type safety and runtime validation no matter what. The config object is also immutable so it will stay safe. Note that the config.get method is not covered, but I don't think it is necessary now that safety is ensured. You might even remove it.