4
votes

I want to add a global property to a Vue 3 application like suggested here

The prototype approach does not work with TypeScript.

I found an example that I converted into this code as config.d.ts

import Vue from 'vue'

import base from '@/config/config.json'
import dev from '@/config/config.dev.json'
import prod from '@/config/config.prod.json'

let config
if (process.env.NODE_ENV === 'production') {
  config = Object.freeze(Object.assign(base, prod))
} else {
  config = Object.freeze(Object.assign(base, dev))
}

declare module 'vue/types/vue' {
  interface Vue {
    $config: config
  }
}

I want to load some local configuration files with dev or prod scope. The scoped files will not be checked into GIT repository.

The main.ts now looks like this...

import Vue from 'vue'
import {createApp} from 'vue'
import App from './App.vue'
import Config from '@/plugins/config.d.ts'

Vue.use(Config)
createApp(App).mount('#app')

The problem:

ERROR in src/main.ts:6:5
TS2339: Property 'use' does not exist on type 'typeof import("d:/Martin/Entwicklung/2020/STA-Electron/node_modules/vue/dist/vue")'.
    4 | import Config from '@/plugins/config.d.ts'
    5 |
  > 6 | Vue.use(Config)
      |     ^^^
    7 | createApp(App).mount('#app')
    8 |

What I ideally want to achieve is a global config or $config property that can be used in the setup method of Vue 3

export default defineComponent({
  name: 'App',
  components: {},
  setup(props, context) {
    const elem = ref(context.$config.prop1)
    const doSth = () => {
      console.log('Config', context.$config)
    }
    return {doSth, elem}
  },
})

How can I fix this?

Update

After the answer from danielv so new plugin looks this

import {App} from 'vue'

export interface ConfigOptions {
  base: any
  dev: any
  prod: any
}

export default {
  install: (app: App, options: ConfigOptions) => {
    app.config.globalProperties.$config =
      process.env.NODE_ENV === 'production'
        ? Object.freeze(Object.assign({}, options.base, options.prod))
        : Object.freeze(Object.assign({}, options.base, options.dev))
  },
}

The main.ts changed also into this

import {createApp} from 'vue'
import App from '@/App.vue'
import ConfigPlugin from '@/plugins/config'
import base from '@/config/config.json'
import dev from '@/config/config.dev.json'
import prod from '@/config/config.prod.json'

createApp(App).use(ConfigPlugin, {base, dev, prod}).mount('#app')

This simple plugin can be used in the template

<template>
  <img alt="Vue logo" src="./assets/logo.png" />
  <p>{{ $config.prop1 }}</p>
</template>

IntelliJ complains about unknown variable prop1, but it works.

I searched a lot but found no way to insert my $config into the setup method that is used with the composition api.

2
"I searched a lot but found no way to insert my $config into the setup method that is used with the composition api." - I missed this part of the conversation, this ends up being a bit of a deal breaker as I now have a global variable which I can use everywhere except in setup logic, for this reason, provide-inject approach is potentially better - Sam

2 Answers

19
votes

You can augment the @vue/runtime-core TypeScript module in your application:

declare module '@vue/runtime-core' {
  interface ComponentCustomProperties {
    $config: Record<string, unknown>;
  }
}

similar to how this used to work in Vue 2.

It seems this has not made it into Vue 3 documentation yet but was introduced with vuejs/vue-next#982. Checkout the ComponentCustomProperties interface source code for more information.

2
votes

In Vue 3, you call the use method on the instance of your app, it's not a method on the Vue var.

const app = createApp(App)
app.mount('#app')
app.use(config)

Also, you need to write your plugin to conform to what Vue 3 expects (an object with install method).

Check out Vue 3 guide on writing and using plugins, it describe a very similar use case to yours.

By the way, *.d.ts files are intended to be used as pure declaration files to declare types, usually as a supplement to otherwise untyped modules (e.g. typing existing js code). You generally don't need to write *.d.ts files yourself when writing Typescript code (declarations can be auto generated by the tsc compiler).