Utilize mapGetter and mapMutations in Vuex with TypeScript without the need for class-style components syntax

After completing a project in Vue, I found myself getting a bit confused without static types. To address this, I decided to incorporate TypeScript into my project while still maintaining the traditional way of writing code, without classes and decorators.

Although I have successfully ported my store to TypeScript while sticking to the conventional approach, I am now faced with a new challenge. I would like to utilize mapGetters, mapMutations, and mapActions in my components using TypeScript, but without resorting to class-style components.

import Vue from 'vue';
import { mapGetters, mapActions } from 'vuex';

export default Vue.extend({
  methods: {
    ...mapActions('pictures', ['toggleFavorite']),
    anotherMethodExample: (pictureId: number): void {
      this.toggleFavorite(pictureId); // Unfortunately, an error occurs at this line.
    },
  },
});

The error message reads as follows:

Property 'toggleFavorite' does not exist on type 'CombinedVueInstance<Vue, unknown, unknown, { index: unknown; isSelected: unknown; getToggleFavorite: any; /* other methods */; }, Readonly<{ picture: any; }>>'.

I am struggling to find a solution on how to use mapGetters and mapActions without utilizing the @Component syntax. Personally, I prefer to stick to the "normal" coding style for better readability and ease of reference to official Vue documentation.

If anyone has advice or suggestions on how to overcome this hurdle, I would greatly appreciate it!

Thank you in advance! o/

Answer №1

After encountering a similar issue, I stumbled upon this query. After conducting some tests, I believe I have found a potential solution.

It appears that the root cause, as evident from the discussions, lies in the need to annotate the computed properties. However, there remains an absence of type safety when injecting the mapped methods from the store module.

The workaround involves wrapping the methods mapGetters and mapActions to allow Typescript to infer the types accurately. This approach will flag compile-time errors if incorrect keys are provided to the mapper, ensuring correct return types without resorting to any.

// defining a type that excludes the first context argument
type OmitActionContext<F> = F extends (
  context: ActionContext<any, any>,
  ...args: infer P
) => infer R
  ? (...args: P) => R
  : never;

// signature of action methods
type ActionMethod = (
  context: ActionContext<any, any>,
  ...args: any[]
) => Promise<any>;

// must include extra parentheses for key inference accuracy
// e.g., mapActionsNamespaced<TYPE>()(namespace, map)
function mapActionsNamespaced<
  S extends Record<keyof S & string, ActionMethod>
>(): <Map extends (keyof S & string)[]>(
  namespace: string,
  map: Map
) => {
  [K in Map[number]]: OmitActionContext<S[K]>;
} {
  return <Map extends (keyof S & string)[]>(namespace: string, map: Map) =>
    mapActions(namespace, map) as any;
}

By utilizing the above wrapper, accurate inference of payload and Promise return types is achieved.

Furthermore, an added benefit is the ability to catch typing errors at compile time should you mistakenly append an 's' to toggleFavorite, triggering an intellisense error on compilation.

...mapActionsNamespaced<PictureModuleActions>()('pictures', ['toggleFavorites'])

For a comprehensive solution, refer to the gist linked here

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

Building a customizable class from scratch

I am currently working on developing configurable classes that come with default values, but allow for configuration changes if needed. The concept involves creating an instance of a class by calling its type specified in the static properties of the Test ...

Updating the parent's reference from a child component in Vue 3

In one of my child components, I have a component named Navbar that includes an option for logging out. <a @click="(event) => {event.preventDefault();}"> Logout </a> This Navbar component has been imported into my parent compon ...

Restricting the data type of a parameter in a TypeScript function based on another parameter's value

interface INavigation { children: string[]; initial: string; } function navigation({ children, initial }: INavigation) { return null } I'm currently working on a function similar to the one above. My goal is to find a way to restrict the initi ...

How can I invoke TypeScript methods within a jQuery event handler in the ngAfterViewInit lifecycle hook?

I am currently utilizing Angular 4. I need to invoke methods from a typescript file within the ngAfterViewInit method. declare var $; @Component({ selector: 'app-details', templateUrl: './details.component.html', styleUrls: [&apo ...

Transform an Array<string> into an object with the strings of the array as keys

I am attempting to develop a type conversion method that transforms an array of strings into an object with the string values as keys. type ObjectFromKeys<T extends Array<string>> = { [K in keyof T]: string } declare const o: ObjectFromKeys& ...

ngx-datatables in Angular is experiencing issues with its filtering options

Having trouble implementing a filter option in ngx-datatables for all columns. I've written the code but it's not functioning correctly. Can anyone help me identify where I went wrong and find solutions? app.component.html: <label> Nam ...

Is it better to convert fields extracted from a JSON string to Date objects or moment.js instances when using Angular and moment.js together?

When working on editing a user profile, the API call returns the following data structure: export class User { username: string; email: string; creationTime: Date; birthDate: Date; } For validating and manipulating the birthDate val ...

Tips to stop scrolling when clicking on a link in a Vue.js carousel

How can I prevent the page from scrolling when clicking on a link in a Vue.js carousel? I've created a carousel card to mimic the ones found on Netflix's main page. It works fine on a single component but when I add multiple carousel components ...

The Vue3 property 'x' is not recognized on type 'Function' as a global property

I have encountered a strange issue with my Quasar app. I am attempting to define a global variable that consists of metadata about the application in an object format. Despite successfully compiling the app and displaying the correct information on the HTM ...

Utilize an Angular HttpInterceptor to invoke a Promise

I have an angular HttpInterceptor and I am in need of invoking an encryption method that is defined as follows: private async encrypt(obj: any): Promise<string> { However, I am unsure of how to handle this within the HttpInterceptor: intercept(req ...

Modifying Array Values in Angular 7

I am currently dealing with a complex array where questions and their corresponding answers are retrieved from a service. Upon receiving the array, I aim to set the 'IsChecked' attribute of the answers to false. The snippet of code I have written ...

Back from the depths of the .filter method encased within the .forEach

I have been working on this code and trying to figure it out through trial and error: let _fk = this.selectedIaReportDiscussedTopic$ .map((discussionTopic) => {return discussionTopic.fk_surveyanswer}) .forEach((fk) => { ...

What is the best way to test chained function calls using sinon?

Here is the code I am currently testing: obj.getTimeSent().getTime(); In this snippet, obj.getTimeSent() returns a Date object, followed by calling the getTime() method on that Date. My attempt to stub this functionality looked like this: const timeStu ...

Unable to construct with cPanel or SSH due to Laravel and Vue integration

Working on my cPanel server with ssh access, I downloaded new changes/files using Git and then executed npm run build. To my surprise, an error popped up. https://i.stack.imgur.com/JaUDG.png Any ideas on how to resolve this error? I double-checked the f ...

Pull in class definitions from the index.js file within the node_modules directory

In my project, I have the package called "diagram-js" in the node_modules. The file node_modules/diagram-js/lib/model/index.js contains multiple class definitions as shown below: /** * @namespace djs.model */ /** * @memberOf djs.model */ /** * The b ...

Best practice for importing pipeable RxJs operators in Angular CLI/WebPack rollup

In the earlier versions of Angular CLI, specifically those before 1.5.0, it was common practice to import all RxJs operators and statics into a single file for easy usage throughout the application. For example: rxjs-operators.ts // Statics import &apos ...

Vue app showcasing array items through search upon button pressing

As I delve into learning Vue Js, I've encountered a bit of confusion. My goal is to showcase array elements from a Rest API based on the specific ID being searched for. For example: within my Json File are various individuals with unique IDs, and when ...

Does npm run use a separate version of TSC?

I am encountering an issue with my VS Code and Node.js project that uses Typescript. Within my package.json file's script block, there is an entry: "build-ts": "tsc" When I run simply tsc on the integrated terminal command line, the compilation proc ...

Populate a div multiple times in Vue by utilizing v-for

Vue may seem like a simple concept, but as a newcomer, I'm still figuring out how to achieve what I want. I've managed to display information from a database query in a div using V-for. However, my goal is to reuse the same div repeatedly by cl ...

The data type 'string' cannot be assigned to type 'E164Number' in the react-phone-number-input component

I'm currently utilizing a library called react-phone-number-input. Within my project, I have two important files: index.tsx and useLogicRegister.ts. While working on my code, I encountered an error stating Type 'string' is not assignable to ...