Skip to content

Actions/functions returned by defineStore option/setup type can be overwritten #2450

@bbugh

Description

@bbugh

Reproduction

https://play.pinia.vuejs.org/#eNqNVUtu2zAQvQqrjWzUFhukzcJ1ArdFGrSLNmiy1EaWxjYTiiRIyh8EXnbXI7SX60k6JCVZcj4oDBgi+fjmzWjm6SH6oFSyriCaRFOTa6YsMWArRXgmludpZE0aXaSClUpqSx5IZeDGnd9YqWHklt+VZVIYv0H2ZKFlSeKEGrdOrInfpyIVOSJq5gA87zMNhggLINnl87BuBA9soPOs+FyJ3J0icjAk5xfkIRWEuGPJIeFyOYiz3FYZ5zuSuQtk0dyYb7KV/72KHeneE1NK/v75RW5XYICYlax4QYTEUECyOQdipXuUa9AbzawFvHLIKllK2VXU0Yfk3cReRgYZv3/WMnKpNeQWM9AZw3V8u1NwqbXUE6K03O5cYckqEwUHTTTK0QIw0YwjeCG1AynQdkdc1CtA2Tr2MY6khyOUk0aY84qJZRqhHAQ+0t5CQ9XTaAWcSw+f0tBI2Da4sFAqnlnAFSHTeWUtJjzLOcvvsb2eqd1giF13rZmwxPfIlIaLT5M8W9gOTd1DHaIp7UiLRlFo8XGZqeTOSIED4VsprQ9wDiahudyeYoJlbgcTt1aZCaVItUv8vpunO5NIvaRhDaYcz7XcGNDInUajhmaGSFrA2krJzThTrE+ZFwLxBXC21okAS4Uq6aM7s7PkXXJCOZtTDESZKGDbD4M3xgWU/8PeQGdvkpO3yRl9jZRp5Ij2OCJYJmtwuBZseVSkXJaKYfvVde4VC4dPbr76PasraGXlK8jvn9i/M9ug9Fpj++s1dFKxmV6CDceXN99gi8/tYSmLiiP6hcMfgMZQOY0B9rHyU9PBebVf/DvHAbg1l1ucctMk5YT6ani8L+2nF1I/yD1NTjtVbNwRC9haqythZXFwGxNFbueeLaCABRPQN1rfYMFjYetxwRp77opj2rk7iP3UxaNjx7TeH5521CNPvUJgcI9gnpiY10Bq+2kudQnrCh/cY9KmPGhcxILBag0bwn1w++PMep+co9xyWQnnb6MgAauPK9O+kkN0DFnvtaLTyOcV7ngvc4f4ypr3jZ8SF7jHdjCbA9/jWtUe1VSrIXT/Pslo/w9+C5iI

Steps to reproduce the bug

  1. Create an options or setup type store with a function in actions or one returned directly in the create function.
  2. In a component, attempt to redefine the function property on the store object created by useStore()
  3. Observe that these "action" functions are successfully overwritten at runtime, and that TypeScript does not complain at compile time.

For example, the following code (additional examples in the playground) will print "actually a bad function bwahahaha!" when clicking on either of the buttons, instead of raising an error.

export const useSetupStore = defineStore('setup', () => {
  const goodFunction = () => {
    console.log('Good setup!');
  };

  return {
    goodFunction,
  };
});

export const useOptionsStore = defineStore('counter', {
  actions: {
    goodFunction() {
      console.log('Good options!');
    },
  },
});
<script setup lang="ts">
import { useSetupStore, useOptionsStore } from './store.ts';

const setupStore = useSetupStore();
const optionsStore = useOptionsStore();

function badFunction() {
  console.log('actually a bad function bwahahaha!');
}

setupStore.goodFunction = badFunction;
optionsStore.goodFunction = badFunction;
</script>

<template>
  <button @click="setupStore.goodFunction()">Print Setup</button>
  <button @click="optionsStore.goodFunction()">Print Options</button>
</template>

Expected behavior

  1. This would be a runtime error (proxy set handler or readonly property type) the way trying to overwrite a getter or computed result does.
  2. TypeScript would complain at compile time that the property is read only because of 1

Actual behavior

The property on the objects can be overwritten without issue, both at compile time and runtime.

Additional information

We encountered this as a bug in a non-contrived situation that took quite a while to track down.

Workaround

Removed, the workaround worked in some circumstances, but with refs and computeds, it would work at compile time with TypeScript and then result in a runtime error of TypeError: Cannot add property _getters, object is not extensible.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions