The open blogging platform. Say no to algorithms and paywalls.

How to get TypeScript type completion by defining process.env types

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 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.




Continue Learning