How to define TypeScript types for process.env to have auto-completion

Andréas Hanss
Oct 7 · 2 min read

Using the process.env function to consume environment variables in its NodeJS program is very frequent, it is a good practice of the « Twelve-Factor App », a manifest for designing applications that scale up automatically.

By default when you install NodeJS TS ambient types (@types/node) in your Javascript project, process.envtypes are defined as such:

interface Process extends EventEmitter {
emitWarning(warning: string | Error, name?: string, ctor?: Function): void;
env: ProcessEnv;
exit(code?: number): never;
}

interface ProcessEnv {
[key: string]: string | undefined;
}

Well, we see that the process object has an env property of type ProcessEnv (an interface). This interface is composed of an object with string keys and string or undefined values.

Great! The typescript compiler will not scream if you type process.env.HOST, on the other hand, you are forced to check the injected environment variables to know which variables are available for use, which is not so cool…

You will agree: we could achieve a better development experience.

Typescript allows overriding ambient definitions of another module very simply by using a concept called « interfaces merging », thanks to which we will be able to define upstream the representation of the values available in process.env.

Override process definitions in NodeJS to have autocompletion

In a file I chose to called modules.d.ts defined in your project root folder, we will define the following things.

declare namespace NodeJS {
export interface ProcessEnv {
HOST: string;
DB_URL: string;
DB_NAME?: string;
}
}

Warning: This module must be within the scope of the project defined in the tsconfig.json using include property so that the compiler can find it and take it into account.

As defined from typescript doc, by default all .d.ts are included.

If the "files" and "include" are both left unspecified, the compiler defaults to including all TypeScript (.ts, .d.ts and .tsx) files in the containing directory and subdirectories except those excluded using the "exclude" property.

Or you can specify them manually using types property, but you lose the benefit of auto-looking from type that is by default looking for all @types folder. That’s why I don’t recommend not using types key.

// Avoid that if possible
{
// …
"types": ["@types/node", "modules.d.ts"]
}

Warning 2: Due to the nature of env vars, all values are string, you should handle conversion in your code.

By now, your compiler and IDE should offer you autocompletion and a good validation.

👋🏻 For a French version of this article 🇫🇷 : let’s check out at my blog : https://modern-javascript.fr/comment-definir-les-types-de-process-env-avec-typescript-pour-avoir/

JavaScript in Plain English

Learn the web's most important programming language.

Andréas Hanss

Written by

👨🏻‍💻I’m JS Tech leads @ Kaliop 🇫🇷 — I work with react, react-native and all modern-js tech — I’m also a 🔥 Firebase addict — https://modern-javascript.fr

JavaScript in Plain English

Learn the web's most important programming language.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade