Incorporate TypeScript to enhance Vue 3 with global properties

Looking to implement a global property in a Vue 3 application as advised here

Unfortunately, the prototype approach doesn't work with TypeScript.

I came across an example that I converted into the following 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
  }
}

Trying to load local configuration files with dev or prod scope without checking them 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 issue:

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 |

My goal is to have a global config or $config property that can be accessed 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 resolve this?

Update

Following danielv's answer, the new plugin looks like 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 update to main.ts has transformed it 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 straightforward plugin can now be utilized in the template

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

IntelliJ may complain about unknown variable prop1, but it functions correctly.

I've looked extensively, yet haven't found a way to integrate my $config into the setup method used in the composition api.

Answer №1

To enhance the @vue/runtime-core TypeScript module within your application:

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

export {}  // It is crucial! Refer to note.

Refer to this documentation: ComponentCustomProperties

Note: TS module augmentation functions accurately only when positioned in the module.

In TypeScript, just like in ECMAScript 2015, any file that has a top-level import or export is categorized as a module. Conversely, a file devoid of any top-level import or export declarations is viewed as a script whose contents are accessible in the global scope (thus to modules too).

Henceforth, the file should have at least one top-level import or export statement, even if it is empty (like in the aforementioned example) - Type Augmentation Placement

Answer №2

To implement plugins in Vue 3, you need to call the use method on the app instance, rather than on the Vue variable itself.

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

Your plugin should follow Vue 3's expectations by providing an object with an install method.

You can refer to the Vue 3 guide for detailed instructions on writing and using plugins, which covers a scenario similar to yours.

By the way, *.d.ts files are primarily used as declaration files to define types, often complementing untyped modules like existing JavaScript code. When working with TypeScript, you typically don't need to manually write *.d.ts files as the compiler can generate declarations automatically.

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

Utilizing VueJS with a non-sequential JSON endpoint

Currently working on a VueJS project, I have a collection of JSON objects accessible through a .json endpoint. They are referred to as People, and by using VueResource, I am able to retrieve an array of people stored in this.people. While I can loop thro ...

Guide to effectively utilizing Vue slot-scope feature within nested components

I am currently facing difficulties in understanding the proper usage of the slot-scope attribute and comprehending the documentation. Below is a simplified version of my requirement. You can view it in action here: https://jsfiddle.net/4j4zsy0g/ Main Cod ...

Creating a personalized 404 page in your Angular Project and configuring a route for it

I am currently working on an Angular project that includes a component named 'wrongRouteComponent' for a custom 404 page. Whenever a user enters a non pre-defined route, the 'wrong-route.component.html' should be displayed. However, I a ...

The expression has been altered following verification. It previously read as 'model: 1777' but now states 'model: 2222'

I've been working on this HTML code that utilizes [(ngModel)] to update input values, and I want the Total, Subtotal, and Amount Paid fields to be automatically calculated when a change is made. However, I'm encountering some issues with this app ...

The element 'HTMLVideoElement' does not support the property 'captureStream'

Currently, I am working on a project that involves using WebRTC in React with typescript. I came across the MDN HTMLMediaElement.captureStream() documentation which I followed. const vid: HTMLVideoElement | null = document.querySelector("video") ...

Executing a function when a user chooses to exit a webpage using the @HostListener('window:beforeunload') method

Utilizing @HostListener('window:beforeunload') allows me to detect when a user navigates away from the page, prompting a dialog window to open. I wish for an event to be triggered or a method to be executed if the user chooses to leave the page. ...

Tips for hiding a sidebar by clicking away from it in JavaScript

My angular application for small devices has a working sidebar toggling feature, but I want the sidebar to close or hide when clicking anywhere on the page (i.e body). .component.html <nav class="sidebar sidebar-offcanvas active" id="sid ...

creating TypeScript model classes is essential to organizing and structuring your

My approach to defining model classes involves using the following structure: export class Company { constructor( public id?: number, public name?: string, public shortName?: string ) {} } I utilize the ? symbol to prevent errors when assi ...

I'm looking for a way to modify the Turkish characters and spaces in the names of JSON data objects. I plan to do this using WebApi

I am facing an issue with fetching data through an API. The JSON data format contains Turkish characters and spaces, causing problems when trying to display the data in a datatable. I have attempted to use the replace and parse functions, but so far, I hav ...

The function startAfter() in Firebase Realtime Database (RTDB) does not seem

I'm currently implementing pagination for a list of items using Vuefire, and encountering an error with the following code snippet (the function works properly with startAt() but not with startAfter()) lastVisible is an object within my component&apo ...

Recursive rendering of tree components in React

I am facing a challenge in rendering tree items recursively using React. I have been struggling to achieve the desired outcome as calling treeRender(children) seems to alter the data structure when a folder is encountered for the first time. I am curious a ...

The missing properties in the TS Type are as follows:

Currently working with Angular 7.x.x and TypeScript version 3.2.4. I have defined two TypeScript interfaces where one extends the other: Main interface: export interface Result { var1: string; var2: number; var3: boolean; } The second ...

Display dates using a 24-hour format with Vue and v-calendar

Having trouble configuring my v-calendar datetime picker to display in 24h format. I have been reviewing the documentation but haven't been successful so far: <v-date-picker is-expanded id="match-date-time" v-model="date" mode ...

How to Retrieve Rectangle Positions on a Canvas

I am facing a specific scenario: I have created a rectangle on the canvas. By using the mouse wheel, the user can zoom in and out based on the position of the mouse cursor. Below is the TypeScript code for zooming: this.context?.clearRect( 0, 0 ...

Ways to obtain a tab and designate it as the default in angular when using angular material Tabs

I am facing an issue with accessing tabs within a nested component. The parent component contains the tab feature and to reach the tabs inside the child component, I am using the following code: document.querySelectorAll('.mat-tab-group'); The a ...

Modifying audio output in a React element

I am trying to incorporate background music into my React app using TypeScript. However, I am encountering an issue where changing the music in the parent component does not affect the sound playing in the child node. import React from 'react'; ...

The Vue application encountered an issue while trying to mount the component due to the absence of a defined template or render function. The error was triggered

Here is the code snippet for my component: <template> <uploader class="uploader-example"> <uploader-unsupport></uploader-unsupport> <uploader-drop> <p>Drop ...

Discovering various kinds of data with a single generic type

I am looking to define a specific type like this: type RenderItems<T> = { [K in keyof T]: { label: string; options: { defaultValue: T[K]['options'][current_index_of_array]; item: (value: T[K][&apo ...

The declaration '() => string' cannot be assigned to type 'string' in a Typescript React program

There have been many questions similar to mine, but none of the answers seem to solve my issue. The closest answer I found was here, however, it also doesn't work for me. I have a Tsx code snippet like this: <img src={getLogo} alt="Airline Lo ...

"Utilizing the v-autocomplete component with on-select and on-remove events in Vuet

Are there any on-select or on-remove properties available in v-autocomplete from Vuetify? I need to manually handle these events. I have tried using @change, but it does not inform me whether an option has been added or removed. <v-autocomplete : ...