Safeguard
Developer Tools•

Why is process.env typed string or undefined, and what to do?

process.env values are strings and may be missing. How TypeScript types them, the 'undefined' string trap, and a safe way to read config in Node.

Safeguard Security Team
3 min read

Environment variables are always strings, and any of them can be missing, so a correct type is string | undefined. Node.js also converts values you assign to process.env into strings, so assigning undefined stores the text "undefined". Read config once, check it, and convert it to real types.

What the Node.js docs say

The process documentation states that assigning a property on process.env implicitly converts the value to a string. It marks this behavior as deprecated and says future versions may throw when the value is not a string, number or boolean. Its own example shows the trap:

process.env.test = undefined;
console.log(process.env.test); // 'undefined'

The docs also say changes to process.env are not reflected outside the process, and that you should use delete to remove a property. On Windows, names are case insensitive, which matters if you share code across operating systems.

Why the type has an undefined in it

TypeScript treats process.env as a dictionary of strings. With noUncheckedIndexedAccess on, a lookup such as env.NODE_ENV is typed string | undefined, as the tsconfig reference explains. Without that flag, the compiler claims the value is always a string. That claim is false the moment someone forgets a variable in a deployment.

Three common mistakes

  1. A non-null assertion. Writing process.env.API_KEY! tells the compiler to stop worrying. The app then fails later, far from the cause.
  2. A silent default. Using process.env.AUTH_MODE ?? "off" means a typo in a deploy file turns authentication off. For security settings, prefer failing at startup over defaulting to the open option.
  3. Truthiness on strings. The string "false" is truthy in JavaScript. A flag such as DEBUG=false can enable debug output if you test it as if (process.env.DEBUG).

A safe pattern

Read all configuration in one module, at startup, and export typed values. Everything else imports from that module and never touches process.env.

function required(name: string): string {
  const value = process.env[name];
  if (value === undefined || value === "") {
    throw new Error("Missing required environment variable: " + name);
  }
  return value;
}

export const config = {
  port: Number(required("PORT")),
  authMode: required("AUTH_MODE"),
};

Add range checks, such as a port number between 1 and 65535, and an allow list for modes. The post on validating environment variables at startup goes further.

Where this connects to secrets

Typing does not make environment variables a safe place for secrets. See where a Node.js app should keep secrets for the trade offs.

Try it yourself

Open the demo and look at how a scan reports what is in a project. A config module is the first thing a reviewer should read, and an inventory shows which packages read the environment.

Click through it at your own pace, or open it full screen.

Safeguard scans dependencies and builds SBOMs for Node.js projects. Run a free scan to see what your lockfile pulls in.

Quick check

What happens if I assign undefined to process.env.X?

Node.js converts it to the string "undefined". Use delete process.env.X to remove a variable.

Is the string "false" falsy?

No. Any non-empty string is truthy in JavaScript. Compare against the exact string you expect.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.