Hone logo
Hone
Problems

TypeScript Environment Type System

In software development, it's common to have different configurations for your application based on the environment it's running in (e.g., development, staging, production). This challenge will guide you in creating a robust and type-safe system in TypeScript for managing these environment-specific configurations. This is crucial for preventing runtime errors, improving developer experience, and ensuring consistency across deployments.

Problem Description

Your task is to design and implement a TypeScript system that allows defining different environment types and retrieving configuration values based on the current environment. This system should be:

  1. Type-Safe: All configuration access should be type-checked at compile time.
  2. Extensible: Easily add new environments and configuration keys.
  3. Readable: The configuration structure should be intuitive and easy to understand.
  4. Environment-Aware: The system should be able to determine the current environment and load the appropriate configuration.

Key Requirements:

  • Define a union type for all possible environments.
  • Create a type that maps each environment to a specific configuration object.
  • Implement a function that takes the current environment as an argument and returns the corresponding configuration object.
  • Ensure that accessing a configuration property on the returned object is type-checked.
  • Consider how to handle missing configuration keys for a specific environment.

Expected Behavior:

When the current environment is provided, the system should return the configuration object tailored for that environment. Any attempts to access properties that don't exist in any environment's configuration should result in a TypeScript compilation error.

Edge Cases:

  • What happens if a configuration key is present in one environment but not another? The type system should guide how this is handled.
  • How would you ensure that a configuration value is always present, regardless of the environment?

Examples

Example 1:

Let's say we have two environments: development and production.

Input:

// Assume we have a function like this:
// const getConfig = (environment: Environment): EnvironmentConfig<Environment> => { ... };

const devConfig = getConfig('development');
const prodConfig = getConfig('production');

Output:

// devConfig would be of type { databaseUrl: string; logLevel: 'debug' | 'info'; apiPort: number; }
// prodConfig would be of type { databaseUrl: string; logLevel: 'warn' | 'error'; apiPort: number; enableCaching: boolean; }

Explanation: The getConfig function, when passed 'development', returns a configuration object with properties databaseUrl, logLevel, and apiPort. When passed 'production', it returns an object with databaseUrl, logLevel, apiPort, and enableCaching. Notice that enableCaching is only available in the production configuration.

Example 2:

Demonstrating type safety and handling missing keys.

Input:

// ... (assuming Environment and EnvironmentConfig types are defined)

const devConfig = getConfig('development');

// Attempting to access a property that only exists in production:
// console.log(devConfig.enableCaching); // This line should produce a TypeScript error.

// Attempting to access a property that doesn't exist in any environment:
// console.log(devConfig.nonExistentProperty); // This line should also produce a TypeScript error.

// Accessing a common property:
console.log(devConfig.logLevel); // This should be fine.

Output: TypeScript compilation errors for devConfig.enableCaching and devConfig.nonExistentProperty. No compilation error for devConfig.logLevel.

Explanation: TypeScript correctly identifies that enableCaching is not part of the development environment's configuration. It also flags nonExistentProperty as it's not defined for any environment in the type definition.

Constraints

  • The solution must be implemented entirely in TypeScript.
  • The system should not rely on runtime environment variable checks for type safety, but rather a compile-time type definition. Runtime checks might be used to determine the environment string, but the configuration structure itself should be dictated by types.
  • The solution should be efficient and not introduce significant overhead.

Notes

  • Consider using mapped types and conditional types to build the EnvironmentConfig type dynamically.
  • Think about how to make common configuration values that are identical across all environments.
  • The problem encourages designing the type system for environments, not just a simple lookup. How can you ensure that certain keys are required in all environments, while others are optional or environment-specific?
Loading editor...
typescript