1

Please take a look at the following code:

const config = {
  prefix: 'MYAPP',
  MYAPP_HOST: '1',
  MYAPP_HOST: '2',
  [ this.prefix + '_HOST' ]: 'localhost',
}

console.log(`${config.MYAPP_HOST}`)
console.log(`${config.${config.prefix}_HOST}`)

What's the proper way to make both outputs to print localhost?

I know one way to make

  [ this.prefix + '_HOST' ]: 'localhost',

working is to define this.prefix as an function, so it becomes:

  [ this_prefix() + '_HOST' ]: 'localhost',

However, I'm hoping that, because the prefix belongs to const config, so that it could be defined within const config, not outside of it.

As for ${config.prefix}_HOST, I'm just trying to construct the string MYAPP_HOST, but nested with template literal. Is there any way to make it possible?

EDIT:

Nested template makes no sense

while it might not be obvious why I need to do this, because the question has been simplified to focus on the technique, here is the a less simplified version of what I want. take a look at the following code:

const config = {
  prefix: 'MYAPP',
  app: { port: 3000 },
  db: { host: 'localhost', port: 27017, name: 'db' },

  url: function () { return `driver:/${process.env.MYAPP_HOST || this.db.host}:${process.env.MYAPP_PORT || this.db.port}/${process.env.MYAPP_NAME || this.db.name}` },
}

and I don't want to use string literal MYAPP in the process.env.MYAPP_... but want to use the prefix variable instead. Makes sense or not aside, this is what I need to do. Now how to do that?

UPDATE:

The answers to the above question has been spreading at many places, so here is the concise summary of the solution (to OP):

const prefix = 'MYAPP'
const config = {
  prefix,
  MYAPP_HOST: '1',
  MYAPP_HOST: '2',
  [ prefix + '_HOST' ]: 'localhost',
}
console.log(`${config.MYAPP_HOST}`)
console.log(`${config[config.prefix+'_HOST']}`)

Thanks everyone, upvoting to your all!

xpt
  • 20,363
  • 37
  • 127
  • 216
  • `config[\`${config.prefix}_HOST\`]` – elclanrs Feb 18 '18 at 02:34
  • @elclanrs, OK. so it is impossible to still define `MYAPP_HOST` within `const config` declaration. Now, how about nested template literal, is it impossible as well? – xpt Feb 18 '18 at 03:09
  • 1
    I don't know if what you want is possible, but `this` at this time is the one of your context (e.g `window` if from global scope). And instead of a function, you could always use a simple variable `const prefix = "MYAPP", config = {prefix: prefix, [prefix + "_HOST"]: 'localhost'}`. – Kaiido Feb 18 '18 at 05:23

3 Answers3

2
const config = {
  prefix: 'MYAPP',
  MYAPP_HOST: '1',
  MYAPP_HOST: '2',
  [ this.prefix + '_HOST' ]: 'localhost',
}

Above would not work as in the time of the declaration of config, this.prefix is undefined. You would get something like following if you log config. Note undefined_HOST.

{prefix: "MYAPP", MYAPP_HOST: "2", undefined_HOST: "localhost"}

Instead try following, if you really want to do it like that,

const config = {
  prefix: 'MYAPP'
}
config[config.prefix+'_HOST']='localhost'
Skyler
  • 656
  • 5
  • 14
  • OK. so it is impossible to still define `MYAPP_HOST` within `const config` declaration. Now, how about nested template literal, is it impossible as well? – xpt Feb 18 '18 at 03:09
  • @xpt Nested template makes no sense. See https://stackoverflow.com/questions/48796938/nesting-template-strings-in-typescript – Estus Flask Feb 18 '18 at 12:04
  • @estus, unfortunately that doesn't answer *my* question. – xpt Feb 18 '18 at 13:37
  • 1
    @xpt OP had exactly same issue as you did. I've added the answer for clarity. – Estus Flask Feb 18 '18 at 17:00
1

There is no such thing as nested template literals because it isn't needed. As explained in this answer, this is achieved with bracket notation, both in template literals and regular JS. It should be:

url: function () {
  return `driver:/${process.env[this.prefix + '_HOST'] || this.db.host}:${process.env[this.prefix + '_PORT'] || this.db.port}/${process.env[this.prefix + '_NAME'] || this.db.name}`
}

Usually there is even no need for that because this template literal is very confusing and hard to read. The purpose of template literals is to provide well-formatted concatenated strings, and this one defies the purpose.

Such expressions can be seen with temporary variables in properly styled third-party code - and yes, it can be achieved without bracket notation then:

url: function () {
  const host = process.env[`${this.prefix}_HOST`] || this.db.host;
  ...
  return `driver:/${host}:${port}/${name}`;
}

Nice, clean and requires no enormously long lines that can cause linter errors.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
1

Your original snippet does not work because you cannot reference properties of the object literal during its definition (as @Skyler also explained).

However, it should work perfectly fine inside that url method. I would write

const config = {
  prefix: 'MYAPP',
  app: { port: 3000 },
  db: { host: 'localhost', port: 27017, name: 'db' },
  prefixed_env(name) {
    return process.env[this.prefix+"_"+name];
    // or, with template literals:
    return process.env[`${this.prefix}_${name}`];
  },
  url() {
    const host = this.prefixed_env("HOST") || this.db.host,
          port = this.prefixed_env("PORT") || this.app.port,
          name = this.prefixed_env("NAME") || this.db.name;
    return `driver:/${host}:${port}/${name}`;
  },
};

Instead of this, you can also refer to config directly.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375