Ensuring type safety while dynamic object property setting in VueX Store using TypeScript

Currently, I am working on a Vue 3 project with TypeScript, transitioning from JavaScript, and keeping it simple without using complex plugins for Vuex store. I have a mutation that dynamically sets various properties of an order object to eliminate the need for writing a mutation for each property. In JavaScript, the mutation looks like this:

 setMetaProp(state, { propName, value }) {
     state.order[propName] = value;
 }

As I continue learning and converting to TypeScript, I have created a function that achieves the same functionality with type safety:

export interface PropNameKeyValue<KeyType, ValueType> {
    propName: KeyType;
    value: ValueType;
}
export declare function setMeta<Key extends keyof Order>(payload: PropNameKeyValue<Key, Order[Key]>): void;

// The type of 'is_quote' is boolean in the 'Order' type declaration

setMeta({ propName: 'is_quote', value: '123' }); 

// If I attempt to set 'is_quote' to something other than a boolean here,
// TypeScript catches the issue and prompts that it should be a boolean
...

My question now is, am I expecting too much in terms of TypeScript and Vuex integration?

Answer №1

Dealing with generics can sometimes pose a challenge.

When using the setMeta function, TypeScript can infer the type because you are invoking this function.

The issue arises in this particular line:

Parameters<Mutations[Key]>[1]

It's difficult to infer the payload without actually calling the functions, especially in this scenario. TypeScript is uncertain about the expected value.

Even from within the setMeta function, you can't use Parameters to infer it:

export declare function setMeta<Key extends keyof Order>(payload: PropNameKeyValue<Key, Order[Key]>): void;

type O = Parameters<typeof setMeta>[0] // PropNameKeyValue<keyof Order, any>

The simplest solution is to create a union type with all allowable values:

type Values<T> = T[keyof T]

type AllowedValues = Values<{
  [Prop in keyof Order]: PropNameKeyValue<Prop, Order[Prop]>
}>

Now it functions as expected:

...

Playground

UPDATE

I have overloaded the commit function.

Regarding state mutations:

TS tends to struggle with mutations in general. Due to objects being contravariant to their keys, string|boolean is resolved to never because string & boolean = never.

For more information, please refer to my article on mutations in TypeScript.

import {
  ActionContext,
  ActionTree,
  MutationTree,
} from 'vuex';

type Order = {
  ...
}

type Artwork = {
  ...
}

type Values<T> = T[keyof T]

type AllowedValues<Type> = Values<{
  [Prop in keyof Type]: {
    propName: Prop;
    value: Type[Prop];
  }
}>

...

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

Querying the api for data using Angular when paginating the table

Currently, I have a table that retrieves data from an API URL, and the data is paginated by default on the server. My goal is to fetch new data when clicking on pages 2, 3, etc., returning the corresponding page's data from the server. I am using an ...

Trouble with formatting credit card numbers in Vue.js

My payment gateway component includes a feature where selecting credit card triggers the _formatCreditCard method to format the credit card number like this: 4444 2442 4342 3434 This is the function in question: _formatCreditCard: function() { var n ...

Can the contents of a JSON file be uploaded using a file upload feature in Angular 6 and read without the need to communicate with an API?

Looking to upload a JSON file via file upload in Angular (using version 6) and read its contents directly within the app, without sending it to an API first. Have been searching for ways to achieve this without success, as most results are geared towards ...

What is the appropriate overload to be selected when utilizing a ref in the method call?

Encountering an unfamiliar TS error while working with the code snippet below: <script lang="ts"> import {defineComponent, computed, toRef} from 'vue' import _ from 'lodash' import {DateTime} from 'luxon' int ...

The "path" parameter must be a string data type in order to proceed. The value received is currently undefined

My current project is utilizing Angular 8 When I attempt to run 'ng build --prod', my project encounters errors. ERROR in The "path" argument must be of type string. Received type undefined The issue arose after adding "enableIvy": true to the ...

Vuex - Avoid changing the vuex store state directly without using mutation handlers or getters

/pages/index.vue computed: { getFirstValue() { return this.$store.state.escapeOnline.slice(0, 1); } } /store/index.js export const state = () => ({ escapeOnline: [{id: 1, name: 'titi'}, {id: 2, 'toto'}], }) Whenever I attem ...

Issues with the typings for the toPromise function in WebStorm have been identified

I'm encountering an issue with WebStorm not recognizing the typings for the toPromise function on 'rxjs', despite having updated it. Is there a way I can troubleshoot this and fix it? Strangely, the code still runs successfully despite the ...

Verify web connectivity in an Angular2 (non-Ionic) Cordova application

Our team has developed an Angular2 Cordova application (not Ionic) that interacts with multiple backend services. We want the app to display a specific page (Component) if a user is offline. Although we have already created this feature, we are unsure of ...

Storing a JWT refreshToken cookie in the response: Best practices

I'm currently in the process of using GraphQL to authenticate a user with JWT. After logging in, I receive a JSON response containing a token and an httponly cookie that stores the refresh token (Saleor-core is used on the server-side). After referri ...

RxJS: when combined with timer, groupBy operator does not emit any values

Just starting out with RxJS version 6.5.5, I'm encountering an issue with the groupBy operator. Here's a simplified example to showcase the problem. I have a function called retrieveFiles() that retrieves an array of strings. function async ret ...

What is the reason behind having to refresh the page or switch to another tab for the field to display?

Currently, I am in the final stages of completing my update form. However, I am facing an issue with the conditional field. The select field should display a conditional field based on the selected value. The problem I'm encountering is that I need to ...

I am facing an issue with the asynchronous function as it is displaying an error message

**I am facing an issue with displaying categories. I have attempted to do this using async function, however the data is not showing up** <div class="form-group"> <label for="category">Category</label> <select id="categor ...

Tests using Cypress for end-to-end testing are failing to execute in continuous integration mode on gitlab.com

Challenges with Setting Up Cypress in Gitlab CI We have been facing difficulties setting up Cypress in the CI runners of gitlab.com using the default blueprint from vue-cli to scaffold the project. Despite trying various configurations in the gitlab.yml f ...

Typescript overloaded function parameters explained

I am currently working on the following code snippet: import React from "react"; interface BaseFormValue { name: string; } export interface NewFormValue extends BaseFormValue { email: string; } export interface ExistingFormValue extends Ba ...

Utilizing CDN for Vuetify in Vue.js and Laravel instead of relying on npm install

I'm looking to reduce the size of my app.js file. Is it possible to import Vuetify from a CDN/UNPKG inside my app.js? Will the load time be the same if I import it locally via npm install compared to using a CDN? I've managed to reduce file size ...

I am experiencing issues with my Vue.js - Cordova app on IOS13, as it seems that incoming requests and internet

As I prepare to release an updated version of my app, I am encountering significant challenges with IOS13. Although the app is functional on IOS13 devices, it is unable to connect to a server or load images from the web. Interestingly, this issue does not ...

An unexpected TypeScript error was encountered in the directory/node_modules/@antv/g6-core/lib/types/index.d.ts file at line 24, column 37. The expected type was

Upon attempting to launch the project post-cloning the repository from GitHub and installing dependencies using yarn install, I encountered an error. Updating react-scripts to the latest version and typescript to 4.1.2 did not resolve the issue. Node v: 1 ...

Default exports are not supported in TypeScript

I'm encountering issues with my Laravel + Vite + Vue 3 project. I followed the installation instructions in the documentation and everything works fine when the project is separated from Laravel and Vite. However, I'm facing a problem where TypeS ...

Vue - Utilizing child slots in the render method

Within my component, I am working with a default slot and attempting to enhance the layout by wrapping each item in the slot within a div. However, I am facing an issue where I need to retrieve the classes of one of the slot elements, but the VNode element ...

Is the Render Function Called Every Time the Component Re-renders?

While it was mentioned that the render function is called every time there's an update, my observations suggest otherwise. I have compiled my findings in this Codepen. Is it possible that the render function is only executed once? render: function (c ...