What is the best approach for associating interfaces with nested objects to ensure proper configuration validation?

Exploring the use of typescript for implementing type checking on a configuration file similar to the one below:

const _config = {
    local: {
        host: 'localhost',
        port: 1234
    },
    dev: {
        host: 'https://dev.myapp.com',
        port: 80
    },
    prod: {
        host: 'https://prod.myapp.com',
        port: 80
    },
}

export const config = _config[process.env.NODE_ENV || 'dev'];

I aim to define an interface for the nested objects to ensure that all configuration properties are present at compile time, rather than runtime:

interface IConfig {
    host: string;
    port: number;
}

const _config = {
    local: { // <-- I want to specify this object as type IConfig
        host: 'localhost',
        port: 1234
    },
    dev: { // <-- I want to specify this object as type IConfig
        host: 'https://dev.myapp.com',
        port: 80
    },
    prod: { // <-- I want to specify this object as type IConfig
        host: 'https://prod.myapp.com',
        port: 80
    },
}

export const config = _config[process.env.NODE_ENV || 'dev'];

Is this achievable? Are there any alternative approaches to accomplish the same objective?

Answer №1

To manage the configuration efficiently, consider using an index signature in combination with an interface.

interface EnvironmentSettings {
    host: string;
    port: number;
}

interface Configuration {
    [name: string]: EnvironmentSettings;
}

const _configuration: Configuration = {
    local: {
        host: 'localhost',
        port: 1234
    },
    dev: {
        host: 'https://dev.myapp.com',
        port: 80
    },
    prod: {
        host: 'https://prod.myapp.com',
        port: 80
    },
}

export const appConfig = _configuration[process.env.NODE_ENV || 'dev'];

Answer №2

Have you considered using an interface to define the structure of the config object itself? This way, you can have different properties for different environments. Here's an example:

interface IConfigProperty {
  host: string
  port: number
}

interface IConfig {
  local: IConfigProperty
  dev: IConfigProperty
  prod: IConfigProperty
}

const _config: IConfig = {
    local: { 
        host: 'localhost',
        port: 1234
    },
    dev: {
        host: 'https://dev.myapp.com',
        port: 80
    },
    prod: { 
        host: 'https://prod.myapp.com',
        port: 80
    },
}

export const config = _config[process.env.NODE_ENV || 'dev'];

Answer №3

To enhance type safety, I suggest using a different approach. By utilizing type aliases, we can specify typings for the host property.

type Host = 'localhost' | 'https://dev.myapp.com' | 'https://prod.myapp.com';

interface Config {
  [name: string]: IConfig;
}

interface IConfig {
    host: Host;
    port: number;
}

const _config: Config = {
    local: { 
        host: 'localhost',
        port: 1234
    },
    dev: {
        host: 'https://dev.myapp.com',
        port: 80
    },
    prod: { 
        host: 'https://prod.myapp.com',
        port: 80
    },
};

If desired, you can also define type aliases for the names of the configurations (local, dev, prod)

Answer №4

To address this issue, one potential solution is to add annotations to the parent object:

interface ISettings {
    apiHost: string;
    apiPort: number;
}

const _settings: { local: ISettings, dev: ISettings, prod: ISettings } = {
    local: {
        apiHost: 'localhost',
        apiPort: 1234
    },
    dev: {
        apiHost: 'https://dev.myapp.com',
        apiPort: 80
    },
    prod: {
        apiHost: 'https://prod.myapp.com',
        apiPort: 80
    },
}

export const settings: ISettings = _settings[process.env.NODE_ENV || 'dev'];

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Exploring the fruitful synergy of Node.js, Mongoose and MongoDB in Typescript for powerful MapReduce operations using the emit() function

Currently, I am experimenting with a side project using the MEAN stack and Typescript. I have encountered an issue where Typescript is not recognizing the typings for the emit() and Array.sum() methods. See my code snippet below... let options: mongoose. ...

Managing AJAX Errors in PHPAJAX error handling tips for developers

My current code is only set up to display a general error message when an error occurs during execution. I want to improve it by returning specific error messages for different scenarios. However, I have not been able to find satisfactory solutions in my s ...

What is the best way to merge two tables together using the server-side JQuery Datatable plugin?

I recently came across an amazing example of a server-side ColdFusion jQuery datatable on this website: Check it out here However, I am facing an issue with adding a second table in the lookup. Specifically, while the primary table lists location_id, I al ...

Angular Application cannot locate the interface file

Attempting to create a basic test environment for the OMBD Api. Utilizing an interface file named ombdresponse.ts: interface IOMBDResponse { Title: string; Year: string; Director: string; Poster: string; } The issue arises within the ombd- ...

Encountering an issue when trying to upload a file for the second time

I am currently working on a project where I need to upload an excel file and send it to an API using ReactJS. So far, I have been able to successfully send the file to the API. However, in my submit function, I want to reset the saved excel file from the s ...

Different ways to modify the color of a chart using am4chart

I am using am4chart to create a line chart on my website. The background of the website is black, so I need to make the chart white. https://i.sstatic.net/eHMw9.jpg I have tried changing the chart fill when creating the chart, but it didn't work at a ...

Having trouble with updating label text in MUIDataTable in ReactJS?

Looking to implement multi-language support in MUI Datatables. I have been able to modify the translations, but when attempting to change languages by providing a different object with new translations (verified using console log), the label texts do not u ...

Tips for creating a script that waits for a specific amount of time before moving on to the next execution block in Protractor

Need to automate a test case that involves filling out a form with 5 date pickers and 30 fields. Once the form is filled, a jar needs to be invoked to retrieve the data from the DB and process it independently. Note: The jar does not send any value back t ...

Is it possible to meta-refresh a page for redirection?

When creating a webpage, I included a META tag like this: <META http-equiv="refresh" content="5;URL=http://www.google.com"> The issue is that mobile browsers do not support this meta tag. It redirects properly on web browsers, but not on mobile dev ...

There was an issue with the v-on handler: "An error occurred because it was unable to read properties of an undefined value (specifically 'input')."

Can anyone help me? When I click the icon inside the avatar, I want to select a file. But I'm getting an error: Error in v-on handler: "TypeError: Cannot read properties of undefined (reading 'input'). Could anyone help me? <v-row v-for=" ...

How does a browser decide to load content from an http URL when the frontend source is using an https URL?

When using my Vue component to load external content in an iframe, everything works fine locally. However, once I deploy it to my HTTPS site, I encounter an issue. <iframe src="https://external-site" /> Upon deployment, I receive the following erro ...

Execute CSS within jQuery code only when the body class is present

I am attempting to use CSS (display: none;) within a script to hide elements from my menu only if a specific language is active. When the body class changes from one language to another, I want the script to check if the body class of a certain language ...

How to create a blinking effect for buttons in an Angular app with ng-if or ng-show

I've come across the same issue in two separate angular projects I've been involved with, but have not been able to find any discussion on this particular problem. This leads me to believe that there may be something I am overlooking. Let's ...

What could be causing this test to fail when testing my API endpoint?

Why am I encountering this error? Uncaught exception: Error: listen EADDRINUSE: address already in use :::3000 import supertest from "supertest" import axios from "axios" import app from ".." const request = supertest(app ...

What could be causing the issue with my customized sorting in the Material-UI Data Grid component?

After integrating Material-UI's Data Grid Component with my API's JSON array, I had to create a RenderCell function to handle text overflow and include a button that directs Users to a different page. Additionally, I utilized the ValueGetter for ...

Will "Access-Control-Allow-Origin" provide protection if someone directs their domain to my server?

It seems I may have misinterpreted the full implementation of CORS on my server. Looking at this screenshot of a request made through Chrome. https://i.sstatic.net/9F1tE.png We can observe that we are accessing the site shakh.photography, where the requ ...

Activate audio when the link is clicked

Recently, I created a compact web application and it's almost complete. However, there is one cool functionality that I am missing. My goal is to have a sound play when the user clicks a link, but after playing, I want the navigation to continue as us ...

Anime.js: Grid layout causing issues with displaying simple text animations

I'm attempting to create a text animation within a grid layout using the anime.js library, but unfortunately, the animation isn't displaying. The project consists of just one HTML file. The JavaScript code: <script> import anime from &ap ...

Upon removing an element, the button click event fails to trigger

I recently created a div that looks like this <div id="hidden"> <img src="photos/Close-2-icon.png" width="16" height="16"> <input id="add" value="Add field" type="button" > <input type='button' id=&a ...

Ensuring TypeORM constraint validations work seamlessly with MySQL and MariaDB

I recently started using TypeORM and I'm trying to incorporate the check decorator in my MySQL/MariaDB database. However, after doing some research on the documentation and online, it seems that the check decorator is not supported for MySQL. I'v ...