简体   繁体   中英

Get object value by dynamic key in typescript

I'm trying to export just 1 value of a config object depending on the process.env.NODE_ENV value, so I'm trying to get the value of the configEnvs variables like configEnvs['local'] but I get an error trying to access one key of the object. I tried using an enum as a key to set it's possible values, but it didn't work.

I'm currently trying to get the key value using getKeyValue which gets the object and limits the key value to only the object's keys, but it then says that the string I'm trying to pass a key is not assignable to the possible options.

const getKeyValue = <T, K extends keyof T>(obj: T, key: K) => obj[key];

const { NODE_ENV, REACT_APP_ENV } = process.env;

interface IConfigEnv {
  DB_DNS: string;
}
interface IConfig {
  local: IConfigEnv;
  development: IConfigEnv;
  production: IConfigEnv;
}

let configEnvs: IConfig = {
  local: {
    DB_DNS: "https://localhost:4000",
  },

  development: {
    DB_DNS: "https://my-dev.web.com",
  },

  production: {
    DB_DNS: "https://api.web.com",
  },
};

const nodeEnv = REACT_APP_ENV || NODE_ENV || "development";
const config: IConfigEnv = getKeyValue(configEnvs, nodeEnv);

export default config; 

I'm getting this error declaring config :

Argument of type 'string' is not assignable to parameter of type '"development" | "production" | "local"'.

I solved this by using Type Assertion like so:

export type NODE_ENVIRONMENTS = "local" | "development" | "production";

// Here using 'as' keyword
const NODE_ENV: NODE_ENVIRONMENTS = process.env.NODE_ENV as NODE_ENVIRONMENTS;
const REACT_APP_ENV: NODE_ENVIRONMENTS = process.env
  .REACT_APP_ENV as NODE_ENVIRONMENTS;

interface IConfigEnv {
  DB_DNS: string;
}

interface IConfigEnv {
  DB_DNS: string;
}
interface IConfig {
  local: IConfigEnv;
  development: IConfigEnv;
  production: IConfigEnv;
}

const config: IConfig = {
  local: {
    DB_DNS: "https://localhost:4000",
  },

  development: {
    DB_DNS: "https://api-dev.web.com",
  },

  production: {
    DB_DNS: "https://api.web.com",
  },
};

const nodeEnv: NODE_ENVIRONMENTS = REACT_APP_ENV || NODE_ENV || "development";

export default config[nodeEnv];

A safer way to do this would be to use something like a typeguard or type assertion function like so:

interface IConfigEnv {
  DB_DNS: string;
}
interface IConfig {
  local: IConfigEnv;
  development: IConfigEnv;
  production: IConfigEnv;
}

const acceptableEnvs = [ "local", "development", "production" ];
function assertEnv(k: any): asserts k is keyof IConfig {
  if (typeof k !== "string" || !acceptableEnvs.includes(k)) {
    throw new Error(
      `Environment must be one of '${acceptableEnvs.join("', '")}'. You passed '${k}'.`
    );
  }
}

const config: IConfig = {
  local: {
    DB_DNS: "https://localhost:4000",
  },

  development: {
    DB_DNS: "https://api-dev.web.com",
  },

  production: {
    DB_DNS: "https://api.web.com",
  },
};

const nodeEnv = process.env.REACT_APP_ENV || process.env.NODE_ENV || "development";

assertEnv(nodeEnv);

export default config[nodeEnv];

Note that this solution makes explicit the error that would have been thrown by your application had you simply type-cast an incorrect runtime value, giving you much more control and certainty over the flow. That said, you'll still have to make sure to do something with that error if/when it is thrown.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM