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
- A non-null assertion. Writing
process.env.API_KEY!tells the compiler to stop worrying. The app then fails later, far from the cause. - 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. - Truthiness on strings. The string "false" is truthy in JavaScript. A flag such as
DEBUG=falsecan enable debug output if you test it asif (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.