Skip to content
DMNO
🚧 DMNO is still in beta! Use with caution!
✨ If you've tried DMNO or looked through the docs, let us know what you think!

TypeScript configuration

Other tools try to infer TypeScript types directly from a configuration schema. While it’s impressive how far this approach has come since the early days, the types used during inference are extremely complex and there are always going to be limitations. Plus importing these config tools directly into your code can introduce headaches with ESM/commonjs and tsconfig files.

Instead, while DMNO still uses TypeScript to define your schema in your .dmno/config.mts files, it is decoupled from your code. We run our own build process using Vite that just works without any configuration problems, and we generate .d.ts files with built-in documentation (JSDoc comments) to be consumed by your code. Our type system is also much more powerful, with an inheritance mechanism not possible in other systems.

Example of VSCode’s IntelliSense using DMNO’s generated types:

Intellisense demo

Accessing the types in your code

To simplify accesing your config, we inject the DMNO_CONFIG and DMNO_PUBLIC_CONFIG globals. We must let TypeScript know about them in order to allow type-checking when using your config and to get autocompletion/IntelliSense in your IDE.

The DMNO Config loader automaticaly generates TS types to be consumed by your code into the .dmno/.typegen directory:

  • .dmno/.typegen/global.d.ts - injects the DMNO_CONFIG global
  • .dmno/.typegen/global-public.d.ts - injects the DMNO_PUBLIC_CONFIG global
  • .dmno/schema.ts - the actual schema of your config, used by the .d.ts files

We use these same types to give you autocompletion when authoring your schema itself.

The easiest way to let TypeScript know about them is to create a dmno-env.d.ts file in your source code that imports them via triple-slash references.

src/dmno-env.d.ts
// inject DMNO_CONFIG global
/// <reference types="../.dmno/.typegen/global.d.ts" />
// inject DMNO_PUBLIC_CONFIG global
/// <reference types="../.dmno/.typegen/global-public.d.ts" />

However in some rare cases - like where you have some set of isolated source files split from the rest of your code, you may need to be a bit more explicit. You can do this by adding references to the generated type files either in your tsconfig or in a specific source file directly.

For example, in your tsconfig:

tsconfig.node.json
{
// ...
"include": [
"vite.config.ts",
".dmno/.typegen/global.d.ts",
".dmno/.typegen/global-public.d.ts",
]
}

Or in a specific file:

vite.config.ts
/// <reference types="./.dmno/.typegen/global.d.ts" />
/// <reference types="./.dmno/.typegen/global-public.d.ts" />
import { defineConfig } from 'vite';
// ...

Pure JS projects

Even if you are not writing TypeScript, these days you are still likely relying on TypeScript for autocompletion in your IDE. If you don’t use a jsconfig.json file, your editor will likely just pick up the dmno-env.d.ts file automatically. But if you do have one, you might need to explicitly add it to your include globs. For example:

jsconfig.json
{
// ...
"include": [
"**/*.js",
".dmno/.typegen/global.d.ts"
".dmno/.typegen/global-public.d.ts"
]
}

Injecting DMNO_CONFIG vs DMNO_PUBLIC_CONFIG

To keep things simple, by default we always inject the global types for both DMNO_CONFIG and DMNO_PUBLIC_CONFIG. However, there are cases where you may want to only inject one or the other:

  • In a front-end only (i.e., non-SSR) context, you could skip injecting DMNO_CONFIG because you’ll only want to use non-sensitive config items. That said, injecting the types doesn’t actually inject your sensitive secrets, and we inject a placeholder proxy throws a helpful error if you try to use DMNO_CONFIG.
  • In a back-end only context, you could skip injecting DMNO_PUBLIC_CONFIG and exclusively use DMNO_CONFIG - just like we do in your .dmno/config.mts file. However there isn’t really any harm in using the public version, and it can serve as an extra check that something is definitely not sensitive - which is useful if you were, for example, returning a config item in an API response.