Utilizing Vue 3's Inject Plugin alongside the powerful Composition API and TypeScript

I developed a controller plugin to be used globally in all components, but I am facing challenges making it compatible with Vue 3 + TypeScript + Composition API as I keep getting a TypeScript error.

ui/plugins/controllers.ts

import { App } from 'vue'
import { provider, IProvider } from '@/core/presentation/provider'

export default {
  install: (app: App) => {
    const controllers: IProvider = provider()
    app.provide('controllers', controllers)
  }
}

main.ts

import { createApp } from 'vue'
import { controllers } from './ui'

createApp(App)
  .use(controllers)
  .mount('#app')

@/core/presentation/provider/provider.ts

import { UserController } from '../controllers'
import { IProvider } from './provider.types'

export const provider = (): IProvider => ({
  users: new UserController()
})

ui/views/Component.vue

import { onMounted, ref, inject, defineComponent } from 'vue'

export default defineComponent({
  setup() {
    const controllers = inject('controllers')

    const user = ref()
    const getUser = async () => {
      const result = await controllers.users.getById(1)
      if (result) {
        user.value = result.toJson()
      }
    }

    onMounted(getUser)

    return {
      user,
      getUser
    }
  }
})

While trying to utilize the controller, I encounter a typescript error at this particular line

const result = await controllers.users.getById(1)

Error:

const controllers: unknown
Object is of type 'unknown'.Vetur(2571)

When attempting to set the type using my interface, it triggers another TypeScript error

import { IProvider } from '@/core'
...
const controllers: IProvider  = inject('controllers')

Error:

type 'IProvider | undefined' is not assignable to type 'IProvider'.
Type 'undefined' is not assignable to type 'IProvider'.Vetur(2322)

The only way that seems to work for me is by doing this which I find quite unconventional:

const controllers: IProvider | undefined = inject('controllers')
...
const result = await controllers?.users.getById(1)

Answer №1

I successfully resolved my issue by referring to the article on Ensuring Type-Safety in Vue.js Injections

It's worth noting that the use of the inject function results in a type union with undefined, as there is a chance that the injection may not be resolved. It's left to your discretion on how to address this scenario.

To handle occurrences of undefined, I followed the suggestion and implemented an injectStrict function. Below is the revised code snippet:

Component.vue

import { IProvider } from '@/core'
import { injectStrict } from '@/ui/utils'
import { onMounted, ref, defineComponent } from 'vue'

export default defineComponent({
  setup() {
    const controllers: IProvider = injectStrict('controllers')
    const user = ref()
    const getUser = async () => {
      const result = await controllers.users.getById(1)
      if (result) {
        user.value = result.toObj()
      }
    }

    onMounted(getUser)

    return {
      user,
      getUser
    }
  }
})

@/utils/injections.ts

import { inject } from 'vue'

function injectStrict<T>(key: string, fallback?: T) {
  const resolved = inject(key, fallback)
  if (!resolved) {
    throw new Error(`Could not resolve ${key}`)
  }

  return resolved
}

export { injectStrict }

Answer №2

When you are confident that the injection will always be resolved, you have the option to utilize the Non-null assertion operator:

const services: IProvider = inject('services')! // <- take note of the exclamation mark!
const result = await services.users.getById(1)

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

Using Vue.js, you can set a method to return data directly to

Having just begun my journey with Vue, I find myself in a bit of a predicament. As part of my learning process, I am developing an app for tracking episodes in TV series. The initial step involves searching for series and adding them to a database. When co ...

How can I ensure thorough test coverage without relying on Testbed?

We have implemented some custom form control components with decorators as follows: @Component({ selector: 'value-selector', templateUrl: './selector.component.html', styleUrls: ['./selector.component.scss'], provid ...

The variable 'selectedvalue' is being accessed before it has been initialized

I'm currently working on sharing the date between components using BehaviorSubject, but I'm encountering an error in the process. public data = new BehaviorSubject<any>(this.selectedValue); public sharedData = this.data.asObservable(); sele ...

Switch up row values in an array and transform them into an object using SheetJS

I am struggling to format an array where each "Working Day" is represented as an object with specific details like index and start/end date. I need help manipulating the JSON data to achieve the desired structure. The package I'm currently using is: ...

Is it necessary to ensure application readiness before proceeding with unit testing exports?

I've been facing a challenge while trying to utilize Jest for unit testing an express API that I've developed. The issue arises when the database needs to be ready before running the test, which doesn't seem to happen seamlessly. In my serve ...

Stop horizontal overflow of content

This unique Vuetify demo on CodePen showcases a two-column layout. The first column contains a <v-list> enclosed in a green <v-alert>. By clicking the "toggle text" button, you can switch the title of the first list item between short and long ...

Typescript React: Implementing type definitions for JSS classes object

Here is the react code I am working with: import React from 'react'; import withStyles from "react-jss"; const styles = { box: { border: "2px solid #000" } }; interface ComponentProps {} class Welcom ...

Troubleshooting V-model errors within VueJS components

Just dipping into VueJS and trying out a chat feature from a tutorial. I noticed the tutorial uses v-model in a component, but when I replicate it, the component doesn't display on the screen and the console throws a "text is not defined" error. Strug ...

Angular 4: Leveraging a directive as a universal constant

I am looking to develop a directive that allows me to utilize a template variable in order to access a global variable, much like $rootScope in Angular.JS. The goal is to avoid having to inject a service into every component where I need access to the vari ...

Ways to modify the CSS of an active class within a child component when clicking on another shared component in angular

In my HTML template, I am encountering an issue with two common components. When I click on the app-header link, its active class is applied. However, when I proceed to click on the side navbar's link, its active class also gets applied. I want to en ...

The error message TS2304 is indicating that the name 'Set' cannot be found in electron-builder

I am trying to utilize the AppUpdater feature in electron-builder for my Electron Application. Upon importing the updater in my main.ts file: import { autoUpdater } from "electron-updater" An error is triggered when running the application: node_module ...

Associate text with a color from a predetermined list (JavaScript)

As I work on adding tags to my website for blog posts, I have a specific vision in mind. Each tag should be assigned a unique background color selected from a predefined array of theme colors. My goal is to assign the same background color to tags with id ...

Leverage Vue's ability to assign data from a parent component to

I am struggling to bind the data (inputData) from the parent component to my child component. I have checked my code multiple times but cannot find where the mistake is. MainApp.js let vm = new Vue({ el: "#app", components: { &ap ...

typescript function intersection types

Encountering challenges with TypeScript, I came across the following simple example: type g = 1 & 2 // never type h = ((x: 1) => 0) & ((x: 2) => 0) // why h not never type i = ((x: 1 & 2) => 0)// why x not never The puzzling part is w ...

Ways to exit a loop in TypeScript

I've encountered an issue with the following code where I am struggling to break the loop under certain conditions. Desired Outcome: An error message should display, allowing the user to close it and remove the escalation node correctly. Actual Outc ...

I am attempting to gather user input for an array while also ensuring that duplicate values are being checked

Can someone assist me with the following issue: https://stackblitz.com/edit/duplicates-aas5zs?file=app%2Fapp.component.ts,app%2Fapp.component.html I am having trouble finding duplicate values and displaying them below. Any guidance would be appreciated. I ...

What is the proper way to utilize e.key and e.target.value in TypeScript when coding with React?

I'm struggling to get e.key and e.target.value working with the following code: const handleInputKeyPress = (e: React.KeyboardEvent<HTMLInputElement> ) => { if(e.key ==='Enter') { dfTextQuery(e.target.value) } }; Why is & ...

Unusual behavior when importing in Angular 2 using TypeScript

While working on a demo for another question on Stack Overflow, I initially used angular-cli and then switched to Plunker. I noticed a peculiar difference in behavior with the import statement between the two setups. The issue arises with the second impo ...

When using a Redux action type with an optional payload property, TypeScript may raise complaints within the reducer

In my react-ts project, I define the following redux action type: type DataItem = { id: string country: string population: number } type DataAction = { type: string, payload?: DataItem } I included an optional payload property because there are tim ...

Sending SQL queries with multiple selections, each containing three values for every option

I am faced with a challenging question that will require me to dedicate long hours towards solving it and finding a solution. Imagine I have a multiple select element with various options, each consisting of 3 values: One value for the language name One ...