Using Typescript with Nuxt can become tricky when attempting to inject data into `Vue.extend` as it appears to be

I want to implement TypeScript support in my Nuxt project.

From my understanding, I need to use Vue.extend when returning the component data like this:

import Vue from 'vue';

type Data = {
 a: number
}

export default Vue.extend({
 data():Data{
  const a = 3;
  return {
   a
  }
 }
})

However, if my component has injected properties, these properties are not attached to the this type.

import Vue from 'vue';

type Data = {
 a: number
}

export default Vue.extend({
 
 inject: ['b'],
 
 data():Data{
  const a = 3;
  const c = this.b
            ^^^^
            // ~~ Property 'b' does not exist on type 'Readonly<Record<never,any>> & Vue' 
  return {
   a
  },
 
  methods:{
   my_method(){
     this.a // no problem here
     this.b // Error here
   }
  }

 }
})

Shouldn't the injected type also be inferred?

I am currently using:

const that = this as any;

which I would prefer to avoid.

Answer №1

If you want to be specific, try explicitly typing this within the data method


import Vue from 'vue';

type Data = {
  a: number
}

export default Vue.extend({

  inject: ['b'],

  data(this: { b: string }): Data {
    const a = 3;
    const c = this.b
    return {
      a
    }
  },

  methods: {
    my_method() {
      this.a // no issue here
      this.b // alright
    }
  }
})

Playground

You can refer to the type definition of Vue.extends:

export interface VueConstructor<V extends Vue = Vue> {
  new <Data = object, Methods = object, Computed = object, PropNames extends string = never>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): CombinedVueInstance<V, Data, Methods, Computed, Record<PropNames, any>>;
  // ideally, the return type should just contain Props, not Record<keyof Props, any>. But TS requires to have Base constructors with the same return type.
  new <Data = object, Methods = object, Computed = object, Props = object>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): CombinedVueInstance<V, Data, Methods, Computed, Record<keyof Props, any>>;
  new (options?: ComponentOptions<V>): CombinedVueInstance<V, object, object, object, Record<keyof object, any>>;

  extend<Data, Methods, Computed, PropNames extends string = never>(options?: ThisTypedComponentOptionsWithArrayProps<V, Data, Methods, Computed, PropNames>): ExtendedVue<V, Data, Methods, Computed, Record<PropNames, any>>;

  // ......
  extend<Data, Methods, Computed, Props>(options?: ThisTypedComponentOptionsWithRecordProps<V, Data, Methods, Computed, Props>): ExtendedVue<V, Data, Methods, Computed, Props>;
  extend<PropNames extends string = never>(definition: FunctionalComponentOptions<Record<PropNames, any>, PropNames[]>): ExtendedVue<V, {}, {}, {}, Record<PropNames, any>>;
  extend<Props>(definition: FunctionalComponentOptions<Props, RecordPropsDefinition<Props>>): ExtendedVue<V, {}, {}, {}, Props>;
  extend(options?: ComponentOptions<V>): ExtendedVue<V, {}, {}, {}, {}>;
}

Therefore, you can provide explicit generics like so:

export default Vue.extend<Data, { my_method: () => void }, object, 'b'>({

  inject: ['b'],

  data(): Data {
    const a = 3;
    const c = this.b
    return {
      a
    }
  },

  methods: {
    my_method() {
      this.a // no problem here
      this.b // ok
    }
  }
})

Answer №2

Utilize the power of vue-class-component alongside vue-property-decorator.

These libraries enable you to write robust TypeScript code as shown below.

@Component({
  layout: 'form',
  head: { title: 'somr title' },
  directives: { onlyNumber: onlyNumberDirective }
})
export default class Login extends Vue {
  @Ref()
  readonly form!: HTMLFormElement;
  
  phone = '';
  password = '';
  
  someMethod() {}

  get someComputedVar() {}
}

Moreover, nuxt-property-decorator combines the aforementioned libraries for even simpler implementation.

Answer №3

I encountered a similar issue and here is the solution I came up with: To resolve this, you need to create a plugin for nuxt as shown below and add it to the plugins section in nuxt.config.

Firstly, create an object using Vue.extend

export const MyService = Vue.extend({

  data(){
    return {
      ready: false
    }
  },

  watch: {
    '$accessor.auth.loggedIn'(success){
        if (!success) return;

         this.ready = true;
        //perform actions here ...
     }
  },

  methods: {
    //...
  },

  
})

Next, export the configure plugin function like this:

export default function (ctx, inject) {

  const hookMixin = {
    created() {
      // nuxt context depends on $root object,
      // so we assign $root which was just created

      const $root = this.$root;
      const myService = new MyService({
        beforeCreate() {
          this.$root = $root;
        }
      });

      //make $myService accessible globally
      Object.defineProperty(Vue.prototype, '$myService', {
        get() { return myService; }
      })

    },
  }

  // add global hook onMounted as mixin
  const mixins = ctx.app.mixins || [];
  mixins.push(hookMixin);
  ctx.app.mixins = mixins;
}

Follow these three steps to make sure this component is globally available.

  1. Register the created hook for nuxt to wait until nuxt is ready.
  2. Assign the nuxt $root object when your component is ready during beforeCreate, which can be set via VueConstructor options.
  3. Make the service global so it can be accessed through $myService using Object.defineProperty.

In this example, I use the watch on the store variable loggedIn. I also utilize typed-vuex, where $accessor serves as a global accessor to the store. The concept is simple - wait for the user to log in before performing certain actions.

Enjoy your custom component!

Answer №4

Could this be Nuxt3?

Remember to modify

import Vue from 'vue'
export default Vue.extend({...})

to

import { defineComponent } from 'vue';
export default defineComponent({ ... })

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

When using Vuejs, it's important to note that the DOM.getBoundingClientRect event does not trigger on window scroll events, but instead

Situation: I am currently developing an infinite scroll feature on a website. The goal is to load more rows in a table when the bottom of the table reaches the bottom of the viewport from outside of it. Implementation: To achieve this, I first declared a ...

Using v-model with the Toast-UI editor in Vue.js adds a dynamic and

Is there a way to bind the input value while using toast-ui vue-editor since it doesn't support v-model? Here is my setup for toast-ui: import '@toast-ui/editor/dist/toastui-editor.css'; import { Editor } from '@toast-ui/vue-editor&apos ...

What is the best way to retain all checkbox selections from a form upon submission?

I have a batch of checkboxes that correspond to the profiles I intend to store in the database. HTML: <tr *ngFor="let profile of profiles | async"> <input type='checkbox' name="profiles" value='{{profile.id}}' ng-model=&apo ...

Connect an input in VueJS to a VueX store state value

As someone new to VueJS, I'm currently working on a VueJS application that provides information about a Github user. For example, you can check out details for . I've set up a store using VueX, but I'm facing an issue with updating the valu ...

Reactive value is failing to update according to the changes in the dependent value

I am working on a project involving Roles and Permissions in Laravel API with a Vue.js frontend. When I click the "All" checkbox on the Add Roles page, all roles are checked but no data appears in inputPermission.permissions const permissions = ref([]) c ...

Is it possible to include a video as a thumbnail before the video begins playing on a webpage? (I am utilizing Laravel and Vue.js for this project

Hello, I have a video ready to play and the specific portion I want to display. However, I am facing an issue with displaying it before the video starts. While using a single image works well (by utilizing video-js and setting the image as a poster), the s ...

Is there a way to showcase a specific chart from Charts.vue within App.vue, even though Charts.vue includes multiple charts in a Vue.js application

I implemented Chartjs to showcase various charts. Within my charts.vue component, I have defined different types of charts. Below is the structure of Charts.vue: <template> <div class="chart-container"> <canvas ref="cha ...

The npm package that utilizes @types/meteor is unable to locate the meteor/meteor module

I recently released an npm package called meteor-model, which has a dependency on @types/meteor. The package itself functions correctly and import Meteor from 'meteor/meteor' resolves accurately to node_modules/@types/meteor However, when I ...

Issue with Multer s3 upload: File not functioning properly in s3 and size increase of file

I successfully uploaded an mp4 file to s3 using node.js and the code seems to be functioning well as I can see the file in s3. However, there seems to be a discrepancy in the file size. The original file is 860kb but after uploading it, it increases to 1.4 ...

Using asynchronous import statements in Vue allows for more efficient loading of components

I am using Vue and have a method in my file called load.js. async function loadTask(x) { return await x; // Some async code } export { loadTask }; In one of my Vue components, I call the method but encounter an issue where the use of await prevents the ...

What is the best way to create a TypeScript function that can return either a string or an object?

I am working with a function that can return either a string or an object. Here is an example: myFunc(path: string): string | object If I already know the exact structure of the object that I am expecting, how can I ensure that the function returns a ty ...

The quantity of elements remains constant in the EventEmitter

The Grid component is structured as follows: export class GridComponent { @Output('modelChanged') modelChangedEmitter = new EventEmitter(); private valueChanged(newValue: any, item: Object, prop: string) { item[prop] = newValue; ...

Looking to compare the values of objects in one array with the values of objects in another array

Within this code snippet, I am attempting to retrieve the id of a variant that aligns with the selected objects const selected = [ { "id": 14 }, { "id": 95 } ] const variants = [ { "id": 1, "option_values": ...

Is it possible for me to access and observe the children attribute within the $ref reference

Is it possible to listen for changes to the children property of an element if the element renders its children later and I have a reference to the element? <element ref="parent"> <child-to-be-loaded-later /> </element> ...

Utilize a dual list approach while setting axios data to useState

I am encountering an issue while trying to fetch data from the backend, as one of my variable names does not conform to the naming convention. When I use setEvents(results.data.events), all fields are retrieved except for one. However, if I attempt to ma ...

I attempted to unsubscribe from an observable in Angular, but I encountered an error stating that the unsubscribe function does not exist

Here is the code snippet from a components.ts file in an Angular project. I encountered the following error during compilation: ERROR merge/merge.component.ts:75:12 - error TS2551: Property 'unsubscribe' does not exist on type 'Observable& ...

Guide on Fetching an Image from a Server with Vue Js

I am trying to fetch an image from the server using Vue.js and Laravel. Below is my code: Controller public function index() { $posts = Post::all(); return response()->json(["posts" => $posts]); } Router Route::get('test','Mas ...

Executing Cross-Component Communication in Angular 7: A Quick Guide

I've encountered a challenge with a checkbox in the header component. If the checkbox is checked, I need to display an alert message when the application loads. The tricky part is that the checkbox is linked to the home component. Therefore, if I am o ...

What is a simple method to convert TypeScript to JavaScript?

Is it possible to eliminate TypeScript-specific keywords from a JavaScript file without using the tsc command, while ensuring that the file remains readable by humans and maintains JSX syntax? ...

How to use attributes in Angular 2 when initializing a class constructor

Is there a way to transfer attributes from a parent component to the constructor of their child components in Angular 2? The process is halfway solved, with attributes being successfully passed to the view. /client/app.ts import {Component, View, bootst ...