Gathering detailed insights and metrics for decoration-vuex
Gathering detailed insights and metrics for decoration-vuex
Gathering detailed insights and metrics for decoration-vuex
Gathering detailed insights and metrics for decoration-vuex
Create type-safe class-based Vuex modules in TypeScript
npm install decoration-vuex
Typescript
Module System
Min. Node Version
Node Version
NPM Version
TypeScript (87.65%)
JavaScript (12.28%)
Shell (0.07%)
Total Downloads
0
Last Day
0
Last Week
0
Last Month
0
Last Year
0
MIT License
1 Stars
97 Commits
1 Watchers
5 Branches
1 Contributors
Updated on Apr 10, 2023
Latest Version
2.2.0
Package Id
decoration-vuex@2.2.0
Unpacked Size
445.71 kB
Size
101.09 kB
File Count
22
NPM Version
6.14.17
Node Version
14.20.0
Cumulative downloads
Total Downloads
Last Day
0%
NaN
Compared to previous day
Last Week
0%
NaN
Compared to previous week
Last Month
0%
NaN
Compared to previous month
Last Year
0%
NaN
Compared to previous year
1
32
Create type-safe class-based Vuex modules in TypeScript
Decoration for Vuex is licensed under the MIT license.
If you want to use Decoration with a bundler like webpack or rollup, or with ts-node;
Install Decoration for Vuex with your favorite package manager;
Install Vuex with your favorite package manager;
Enable experimental decorator support in TypeScript
tsconfig.json
:
1{ 2 "compilerOptions": { 3 "experimentalDecorators": true 4 } 5}
--experimentalDecorators
.If you wish to use Decoration in an environment that does not use a bundler or modules; you have two options
decoration-vuex.iife.js
file from the latest release and include it in your project.https://unpkg.com/decoration-vuex/dist/index.iife.js
or
https://unpkg.com/decoration-vuex@{version}/dist/index.iife.js
. See UNPKG for more versioning
options.https://cdn.jsdelivr.net/npm/decoration-vuex/dist/index.iife.js
or
https://cdn.jsdelivr.net/npm/decoration-vuex@{version}/dist/index.iife.js
. See JSDELIVR
for more versioning options.With Decoration, Vuex modules are written as classes and decorated to indicate what certain members will do.
1// store.ts 2import { Store } from "vuex"; 3 4export const store = new Store({});
1// counter.ts 2import { Module, StoreModule, Mutation } from "decoration-vuex"; 3import { store } from "./store"; 4 5@Module 6class Counter extends StoreModule { 7 count = 0; 8 9 @Mutation 10 increment(): void { 11 this.count++; 12 } 13} 14 15export const counter = new Counter({ store });
Decoration modules are accessed in Vue component by directly referencing the module instance. This even includes any
state, getters, mutations, or actions mapped into the component. The mapState
, mapGetters
, mapMutations
, and
mapActions
helpers are not needed, but are supported.
1// MyComponent.vue (script part) 2import { counter } from "./counter.ts" 3 4export default Vue.extend({ 5 name: "MyComponent", 6 computed: { 7 count(): number { return counter.count }, 8 }, 9 methods: { 10 increment(): void { counter.increment() }, 11 }, 12});
When creating a module; if you define a module constructor, it must receive a RegisterOptions
object and pass it on
the super constructor.
1import { Module, StoreModule, RegisterOptions } from "decoration-vuex"; 2 3@Module 4class Counter extends StoreModule { 5 count: number; 6 7 constructor(options: RegisterOptions, initialCount = 0) { 8 super(options); 9 10 this.count = initialCount; 11 } 12}
The state of a Decoration defined module is the properties assigned to it at construction or when defining the class.
As with the state of a regular Vuex module, the state will become part of the store when registered. It will them become
reactive and must follow the same rules as regular Vuex state. New properties may not be added after instantiation, when
registration occurs, nor should they be removed. By requiring all modules extend StoreModule
, this ensures the state
is created as if it is a plain object.
1import { Module, StoreModule, RegisterOptions } from "decoration-vuex"; 2 3@Module 4class Counter extends StoreModule { 5 count: number; 6 resets = 0; // This is part of the state of the module. 7 8 constructor(options: RegisterOptions, initialCount = 0) { 9 super(options); 10 11 // This is part of the state of the module. 12 this.count = initialCount; 13 } 14}
All normal properties will become part of the state of the module. The only exception is any property defined with get
and set
or using ECMAScript Symbol
s. get
and set
are used to define other features of a module. Symbols
are
ignored by Vuex and provide a means to store any data you don't want to be reactive.
Also, any other method defined in the class may be utilized by the constructor since the module is not yet registered to Vuex.
1import { Module, StoreModule, RegisterOptions } from "decoration-vuex"; 2 3const RESETS = Symbol("resets"); 4 5@Module 6class Counter extends StoreModule { 7 count: number; 8 [RESETS] = 0; // This will not be part of the state, but is accessible anywhere within the module or outside 9 // with the symbol. 10 11 constructor(options: RegisterOptions, initialCount = 0) { 12 super(options); 13 14 // This is part of the state of the module. 15 this.count = initialCount; 16 } 17 18 // This will not be part of the state, but will be part of the module's `getters` in Vuex. 19 get reset(): number { 20 return [RESETS]; 21 } 22}
As with normal Vuex modules, the state cannot be altered outside a mutation.
Accessing the state within a Vue component or even other code is as simple as referencing the module instance.
1import { counter } from "./counter"; 2 3console.log(counter.count); // 0 4counter.increment(); 5console.log(counter.count); // 1 6 7Vue.extend({ 8 computed: { count(): number { return counter.count } }, 9 methods: { increment(): void { return counter.increment() } }, 10 mounted() { 11 console.log(this.count); // n 12 this.increment(); 13 console.log(this.count); // n + 1 14 } 15})
The only way to actually change the state of a module with using a mutation. In Decoration, mutations are decorated
with the @Mutation
. Within a mutation you may read and alter any state of the module. Mutations may even receive
parameters, known as a payload.
1@Module 2class Counter extends StoreModule { 3 count = 0; 4 5 @Mutation 6 reset(): void { 7 this.count = 0; 8 } 9 10 @Mutation 11 increment(by = 1): void { 12 this.count += by; 13 } 14}
Mutations may also access property getters and setters and call other mutations defined on the class.
1@Module 2class Counter extends StoreModule { 3 count = 0; 4 5 get next() { 6 return this.count + 1; 7 } 8 9 @Mutation 10 goNext(): void { 11 // Access the next getter. 12 this.value = this.next; 13 } 14}
There are times when you need to get computed information based on the state of the store using getters. In Decoration, getters are defined in two ways. Simplest as property getters and for more functionality, you can use getter functions.
Property getters are simple ECMAScript getters, which may be paired with a setter. As with Vuex getters, they may not alter the state, but can read any part of it. They may also access other getters, both properties and functions.
1@Module 2class Counter extends StoreModule { 3 count = 0; 4 5 get next() { 6 return this.count + 1; 7 } 8 9 get square() { 10 return this.count * this.count; 11 } 12 13 get halfSquare() { 14 // Accessing the square getter 15 return this.square / 2; 16 } 17}
Sometimes you need to provide more information to a getter to complete its calculation. This is where getter functions
come in. In Decoration, getter methods are decorated with @Getter
. They may to read the state and call other
getters, but may take inputs to complete their job.
1@Module 2class Counter extends StoreModule { 3 count = 3; 4 5 @Getter 6 pow(by: number) { 7 return this.count ** pow; 8 } 9} 10 11const counter = new Counter({ store }); 12 13console.log(counter.pow(2)); // 9
The same rules that apply to getter properties apply to getter functions.
If you want to make a getter property read and write; you will have to provide a setter for that property. Setters are just property setters that have been registered as a mutation in Vuex. They also must follow the same rules as mutations but will provide an improved experience when used.
1@Module 2class Counter extends StoreModule { 3 count = 0; 4 5 get next() { 6 return this.count + 1; 7 } 8 9 get at(): number { 10 return this.count; 11 } 12 13 set at(value: number) { 14 this.count = value; 15 } 16} 17 18const counter = new Counter({ store }); 19counter.at = 7; 20 21console.log(counter.at); // 7
Actions in Decoration work the same as in Vuex with little difference. In Decoration, actions are decorated with
@Action
. They can read the state, use getters, call mutations, and assign to properties that use setters. They can
also call other actions. As with Vuex, actions may be asynchronous and must return a Promise
.
1@Module 2class Counter extends StoreModule { 3 count = 0; 4 5 get next() { 6 return this.count + 1; 7 } 8 9 get at(): number { 10 return this.count; 11 } 12 13 set at(value: number) { 14 this.count = value; 15 } 16 17 @Action 18 async getServerValue(): Promise<number> { 19 const newCount = await fetch("https://example.com/count"); 20 this.at = newCount; 21 22 return newCount; 23 } 24} 25 26const counter = new Counter({ store }); 27 28// If the server returns 5, then log will print 5. 29counter.getServerValue().then(value => console.log(value));
Vuex module may reference other modules for convenience and maintainability. This is required for modules to interact with each other but can convey a relationship between modules. To define a sub-module in Decoration, just assign an instance of the module to the member of another.
1@Module 2class WaitModule extends StoreModule { 3 waiting = false; 4 5 @Action 6 async while(op: () => Promise<void>): Promise<void> { 7 try { 8 this.waiting = true; 9 await op(); 10 } finally { 11 this.waiting = false; 12 } 13 } 14} 15 16@Module 17class ApiModule extends StoreModule { 18 wait = new WaitModule({ store }); 19 20 posts = [] as Post[]; 21 22 @Mutation 23 refreshPosts(posts: Post[]): void { 24 this.posts = posts; 25 } 26 27 @Action 28 async fetchPosts(): Promise<Post[]> { 29 this.refreshPosts(await this.wait.while(fetch("/posts/all"))); 30 } 31} 32 33const api = new ApiModule({ store });
Vuex modules are meant to provide a source of truth for your application state, but can also do the same for any logic related to that state. Decoration allows the use of undecorated methods with the module class for such a purpose. These local functions may be used to write common logic used by other parts of the store.
1@Module 2class Counter extends StoreModule { 3 count = 0; 4 5 get next() { 6 return this.count + 1; 7 } 8 9 get at(): number { 10 return this.count; 11 } 12 13 set at(value: number) { 14 this.count = value; 15 } 16 17 @Action 18 async syncWithServer(): Promise<number> { 19 this.at = await this.getServerValue(); 20 } 21 22 @Action 23 async isInSyncWithServer(): Promise<boolean> { 24 const server = await this.getServerValue(); 25 26 return server === this.at; 27 } 28 29 private getServerValue(): Promise<number> { 30 return fetch("https://example.com/count"); 31 } 32}
Although local functions can be powerful; and may use other public functions, there are a few caveats to be aware of using local functions.
Local functions cannot be called publicly
They may only be used by other methods in the class and should be marked private
.
Local functions must obey the same rules as the caller
For example; if you call a local function within a mutation, that function cannot call an action.
Watches provide a more straight forward means to watch any specific state on a module. Simply decorate a function with
@Watch
and provide it with the path of the state to watch. You may provide additional options from
Vue vm.$watch
method.
1@Module 2class Counter extends StoreModule { 3 count = 0; 4 5 get at(): number { 6 return this.count; 7 } 8 9 set at(value: number) { 10 this.count = value; 11 } 12 13 @Watch("count") 14 onCountChanged(value: number) { 15 console.log(value); 16 } 17}
Watchers may provide powerful capabilities, but may be better served by other functions of a module. The following caveats should be considered when using watchers.
this
Watchers are best used for debugging, logging, and back-end storage features. Setters, mutations, and actions should be used to handle most other use-cases.
A module may inherit functionality from another class, provided that it eventually inherits StoreModule
. It is
currently untested whether the base classes may be decorated with @Module
, but the bottom most class that will be
registered with the Vuex store must be. All members in the base classes should be decorated based on their indented use
case.
1// Not decorated 2class BaseModule<T extends object> extends StoreModule { 3 items: T[]; 4 current: T; 5 6 @Mutation 7 show(item: T) { this.current = item } 8 9 @Mutation 10 refresh(items: T[]) { this.items = items } 11 12 @Action 13 async get(id: string): Promise<T> { this.show(await this.query({ id })[0]) } 14 15 @Action 16 async all(selector: Partial<T>) { this.refresh(await this.query()) } 17 18 @Action 19 async query(selector: Partial<T>): Promise<T[]> { /* ... */ } 20} 21 22@Module 23class ProductModule extends BaseModule<Product> { 24 25} 26 27const products = new ProductModule({ store });
The following options may be provided when decorating a class as a module.
1export interface ModuleOptions { 2 /** Makes the state publicly mutable by defining setters for each top level property */ 3 openState?: boolean; 4}
An open state module is a module that allows directly mutating the state publicly or from actions. This is done by registering mutations for each top level field of the class. This provides a simple and convenient means to expose the module state to alteration, but lessens the safety of have a protected state must be altered with explicit setters and mutations.
1@Module({ openState: true }) 2class Counter extends StoreModule { 3 count = 0; 4 5 @Action 6 async syncWithServer(): Promise<number> { 7 this.count = await fetch(/* ... */); 8 } 9} 10 11const counter = new Counter({ store }); 12 13console.log(counter.count); // 0 14counter.count = 5; 15console.log(counter.count); // 5
Although having an open state is useful, it is only effective for top-level properties. Any properties of objects at the top-level of the module will still be protected by Vuex.
1@Module({ openState: true }) 2class Counter extends StoreModule { 3 data = { 4 count: 0, 5 lastUpdated: now(), 6 }; 7 8 @Action 9 async syncWithServer(): Promise<number> { 10 // ERROR! Cannot modify state outside of a mutation 11 this.data.count = await fetch(/* ... */); 12 } 13} 14 15const counter = new Counter({ store }); 16 17console.log(counter.count); // 0 18// ERROR! Cannot modify state outside of a mutation 19counter.data.count = 5; 20console.log(counter.count); // 0
In general, open state should be avoided except on simplest of modules.
The following options can or must be provided when instantiating a module. At minimal, the store
must be provided.
1export interface RegisterOptions { 2 /** The store to which the module will be registered */ 3 store: Store<any>; 4 /** Optionally rename the module; otherwise, its class name and a unique ID are used */ 5 name?: string; 6}
To maintain the safety and features of Vuex module when defining them with Decoration, certain members of a module may be off limits when interacted with publicly or from decorated methods. The following methods will only have access to the listed features.
Public consumer
Can read the state and call all other methods except local functions or watchers.
Getters
Can do the following
Mutations and setters
Can do the following
Actions
Can do the following
If the module is open state, then altering the top-level of the state is possible.
Local functions
Must abide to the same limits as the caller.
Watchers
Has access to the public interface, but should avoid interacting with the module.
state
, getters
, commit
, or dispatch
is not longer supported. Modules should
be used directly.There is currently no roadmap per se for Decoration, but the PLANS file tracks ideas for the future of Decoration. If an issue was filed, it may appear in the plans file.
No vulnerabilities found.
Reason
no dangerous workflow patterns detected
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
0 existing vulnerabilities detected
Reason
Found 0/29 approved changesets -- score normalized to 0
Reason
project is archived
Details
Reason
dependency not pinned by hash detected -- score normalized to 0
Details
Reason
detected GitHub workflow tokens with excessive permissions
Details
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
Project has not signed or included provenance with any releases.
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Score
Last Scanned on 2025-07-14
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More