'Add global properties to Vue 3 using TypeScript

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.



Solution 1:[1]

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

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

export {}  // Important! See note.

Here is a documentation: ComponentCustomProperties

Important note: TS module augmentation works correctly only when it is placed in the module.

In TypeScript, just as in ECMAScript 2015, any file containing a top-level import or export is considered a module. Conversely, a file without any top-level import or export declarations is treated as a script whose contents are available in the global scope (and therefore to modules as well).

Therefore the file must contain at least one top-level import or export statement, even if empty one (as in the example above) - Type Augmentation Placement

Solution 2:[2]

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).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Michal Levý
Solution 2 danielv