Understanding and Handling `undefined` in TypeScript Environment Variables

In TypeScript, environment variables are crucial for configuring applications across different deployment environments such as development, staging, and production. However, one common issue developers face is dealing with undefined values in these environment variables. This blog post will explore the fundamental concepts, usage methods, common practices, and best practices related to undefined in TypeScript environment variables.

Table of Contents#

  1. Fundamental Concepts
  2. Usage Methods
  3. Common Practices
  4. Best Practices
  5. Conclusion
  6. References

Fundamental Concepts#

What are Environment Variables?#

Environment variables are dynamic-values that can affect the way running processes will behave on a computer. In a TypeScript application, they are often used to store configuration information like API keys, database connection strings, and other sensitive or environment-specific data.

undefined in TypeScript#

In TypeScript, undefined is a primitive type that represents the absence of a value. When an environment variable is not set, accessing it will result in an undefined value. For example:

// Assume this environment variable is not set
const apiKey = process.env.API_KEY;
console.log(typeof apiKey); // Output: 'undefined'

Usage Methods#

Accessing Environment Variables#

In a Node.js application written in TypeScript, environment variables are accessed through the process.env object. Here is a simple example:

// Access an environment variable
const port = process.env.PORT;
if (port) {
    console.log(`Server will run on port ${port}`);
} else {
    console.log('PORT environment variable is not set.');
}

Typing Environment Variables#

To avoid issues with undefined and to make the code more type-safe, we can define types for environment variables. For example:

// Define a type for environment variables
interface EnvironmentVariables {
    PORT?: string;
    API_KEY?: string;
}
 
const env: EnvironmentVariables = process.env as EnvironmentVariables;
 
if (env.API_KEY) {
    console.log(`API key is set: ${env.API_KEY}`);
} else {
    console.log('API key is not set.');
}

Common Practices#

Default Values#

One common practice is to provide default values for environment variables in case they are undefined. This ensures that the application can still function even if the variables are not set.

const port = process.env.PORT || '3000';
console.log(`Server will run on port ${port}`);

Validation#

Another common practice is to validate environment variables early in the application startup process. This helps catch missing or invalid values before the application starts.

function validateEnv() {
    const apiKey = process.env.API_KEY;
    if (!apiKey) {
        throw new Error('API_KEY environment variable is not set.');
    }
    return true;
}
 
try {
    if (validateEnv()) {
        console.log('Environment variables are valid.');
    }
} catch (error) {
    console.error(error);
}

Best Practices#

Use a Configuration Module#

Create a separate configuration module to handle environment variables. This makes the code more organized and easier to maintain.

// config.ts
interface Config {
    port: string;
    apiKey: string;
}
 
function getConfig(): Config {
    const port = process.env.PORT || '3000';
    const apiKey = process.env.API_KEY;
    if (!apiKey) {
        throw new Error('API_KEY environment variable is not set.');
    }
    return { port, apiKey };
}
 
export const config = getConfig();
// main.ts
import { config } from './config';
 
console.log(`Server will run on port ${config.port}`);
console.log(`API key: ${config.apiKey}`);

Use a Validation Library#

There are several validation libraries available for TypeScript, such as joi or yup. These libraries can be used to validate environment variables more comprehensively.

import Joi from 'joi';
 
const envSchema = Joi.object({
    PORT: Joi.string().default('3000'),
    API_KEY: Joi.string().required()
}).unknown(true);
 
const { error, value } = envSchema.validate(process.env);
if (error) {
    throw new Error(`Environment variable validation error: ${error.message}`);
}
 
const env = value;
console.log(`Port: ${env.PORT}`);
console.log(`API key: ${env.API_KEY}`);

Conclusion#

Dealing with undefined in TypeScript environment variables is an important aspect of building robust applications. By understanding the fundamental concepts, using proper usage methods, following common practices, and adopting best practices, developers can ensure that their applications handle environment variables gracefully and are less prone to errors caused by missing or invalid values.

References#