Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write Stores in Typescript #564

Open
Anonyfox opened this issue Jan 4, 2017 · 88 comments
Open

Write Stores in Typescript #564

Anonyfox opened this issue Jan 4, 2017 · 88 comments
Labels
types Related to typings only

Comments

@Anonyfox
Copy link

Anonyfox commented Jan 4, 2017

I couldn't find anything on google, so is this conveniently possible? Basically the bummer for me is that I have to call dispatch/commit with a given identifier string to trigger something.

My vision is to define stores as Typescript classes with typed methods (getting intellisense in editor and so on) which I can call instead of dispatch("my_action", data) and having to look up each and everything and check for errors manually all the time.

Basically my problem is that my team is building a fairly large Vue/Vuex frontend for a core product of our company, consisting of already 18 fairly complex Stores as modules, and this year stuff is going to at least quadruple in size and complexity. We already decided to go on with typescript instead of ES7 and are in the process of migrating our backend/frontend code, but this thing really feels like a bummer.

I think this is not the typical "small" use case for Vue.js, since we're building a huge enterprise frontend with loads of requirements and edge cases, but isn't vuex(/flux) supposed to scale up when complexity rises?

Has anyone experience in building complex, type safe Vuex 2.0 stores in Typescript yet? Any help here would be appreciated

@LinusBorg
Copy link
Member

/ping @ktsn

@ktsn
Copy link
Member

ktsn commented Jan 4, 2017

Thank you for trying Vuex with TypeScript.
Honestly, it is challenge to achieve type safety with pure Vuex API. But we currently can specify action/mutation type as type parameter to let TS compiler can infer the correct type.

// Declare each action type
type ActionA = {
  type: 'A',
  value: number
}

type ActionB = {
  type: 'B',
  value: string
}

// Declare the union type of actions
type Action = ActionA | ActionB

// Provide action type
store.dispatch<Action>({
  type: 'A',
  value: 1
})

About defining the store as class, I believe we can make such binding like vue-class-component and I personally interested in it. Also, there is an experiment for type safety of Vuex. https://github.com/HerringtonDarkholme/kilimanjaro

@Anonyfox
Copy link
Author

Anonyfox commented Jan 4, 2017

Mh, okay, this might be a start. For better clarification, I'll provide a simplified example for a store that will later get included as a module:

state.ts:

export class State {
    // persons above 24 years old
    adults: number = 2

    // persons between 16 and 24 years old
    juveniles: number = 0

    // persons below 16 years old
    children: number = 0
}

mutations.ts:

import { State } from './state'

export type personIdentifier = 'adults' | 'juveniles' | 'children'

export class Mutations {
    // increment the chosen persons type by one
    inc (state: State, key: personIdentifier) {
        state[key]++
    }

    // decrement the chosen persons type by one
    dec (state: State, key: personIdentifier) {
        state[key]--
    }
}

actions.ts:

import { Store } from 'vuex'
import { State } from './state'

export class Actions {
    inc ({ commit }: Store<State>) {
        // ??????
    }
}

skipping the getters for now, since they're unimportant. This is where I am currently, and I would combine these classes into a Store with namespaced: true, since this store might be used on multiple places independently for several UI components.

Is there a solution how I might write these actions as type safe methods? Your example is not clear enough for me to apply the pattern you provided, I think.

@ktsn
Copy link
Member

ktsn commented Jan 4, 2017

Hm, there maybe no solution when namespaced: true is used since we cannot combine string literal type 😞
I guess we need some wrapper to trick the type checking.

@Anonyfox
Copy link
Author

Anonyfox commented Jan 6, 2017

small heads-up: it was really straightforward to implement a Store-Module as a standalone npm package in typescript, with tooling/tests/typings and namespaced:true, and then use it dynamically within the core app.

Thanks to the recent efforts of you guys for the typescript support, implementing the interfaces really helped to get it right immediately!

In my opinion the only puzzle-piece left is typesafe commit/dispatch (with namespaced), but I think this is a hard problem to bolt-on vuex as-is. One idea would be to generate types on the "new Vuex.Store" call in the main code, which map the generated identifier-string to the underlying function or sth like that. But ideally, there could be an alternate way to call commit/dispatch, maybe through wrapper classes to Module that does the actual calling behind the scenes or something. This seems to be an architectural issue, which is really not easy to resolve.

On the other hand I think it would really, really great to solve this. Even if approx. most vuex users do "just" use ES7, they could benefit from intellisense/hints from their Editors/IDEs greatly, providing huge ergonomic value once stores become nontrivial in size.

@morhi
Copy link

morhi commented Jan 17, 2017

@Anonyfox Would you mind creating an example repository or snippet for vuex with typescript, modules and classes as far as you got it so far? I am currently setting up a vue project which probably will contain a few store modules. I understand using vuex with ES6 but I am struggling with properly setting up my project with typescript. I would like to have everything structured well but it seems a bit complicated at the moment :)

As for now I created two global files (mutations.ts and actions.ts) where I export some constants of the mutation types and actions that all stores use. These files are imported both in the store modules and in the components that use the store so I don't need to use string as identifiers.

@Anonyfox
Copy link
Author

@morhi sure, I extracted a simple (but not trivial) store we're using.

https://github.com/Anonyfox/vuex-store-module-example

please respect that I can not give a valid license, this is just intended for demonstrating.

/cc @ktsn

@wonderful-panda
Copy link

I think it would be nice if we can use namespaced module like as

/*
 * equivalent to ctx.commit("foo/bar/action", arg)
 */
ctx.commit.foo.bar("action", arg);
// OR
ctx.foo.bar.commit("action", arg);
// OR
ctx.modules.foo.bar.commit("action", arg);

/*
 * equivalent to ctx.getters("foo/bar/prop")
 */
ctx.getters.foo.bar.prop
// OR
ctx.foo.bar.getters.prop
// OR
ctx.modules.foo.bar.getters.prop

This will make adding and combining types more easy.

@morhi
Copy link

morhi commented Jan 19, 2017

@Anonyfox wow! thank you very much! I adopted your example into my environment and got it working 👍

@Glidias
Copy link

Glidias commented Jan 24, 2017

Related: #532

One way to ensure type consnistency between dispatch/commit payload vs. action/mutation handler, is to declare both of them at once. Eg.

http://tinyurl.com/go9ap5u

However, you'd need some sort of runtime decoration approach to setup necessary pre-initializations prior to app running. Also, Typescript doesn't seem to complain if I override a method with a different payload type parameter form, which is something that i'd need to enforce, though (anyway to do this?). Overall, setting up all the decorators and custom initilizations at runtime is a lot of work.

@wonderful-panda
Copy link

FYI, I wrote helper to make store half-decent type safe.
https://gist.github.com/wonderful-panda/46c072497f8731a2bde28da40e9ea2d7

It seems to work well except namespaced module.

@Glidias
Copy link

Glidias commented Jan 26, 2017

@wonderful-panda Is it possible with your code to add modules within modules recursively?

@wonderful-panda
Copy link

@Glidias yes.

Nested module example is below:

const module1 = builder.createModule<...>({ /* module1 definition */ });
const module2 = builder.createModule<...>({ /* module2 definition */ });

const parentModule = 
    builder.addModule("bar", module1)
           .addModule("baz", module2)    // { modules: { bar: module1, baz: module2 } }
           .createModule<...>({ /* parentModule definition without `modules` */ });

const store =
    builder.addModule("foo", parentModule)
           .createStore<...>({ /* store options without `modules` */ });

@Glidias
Copy link

Glidias commented Jan 27, 2017

@wonderful-panda See the comments in the gist for continued discussion in that area.

Regarding dynamic namespacing of modules + strict typing, one idea i was considering is that after a module tree is initialized, you traverse through it's state tree and will always set a _ property under each module state within the tree (including root store state for the sake of homogeneity), in order to have their namespace path references stored dynamically at runtime. That way, you can simply use rootState.moduleA.moduleB._, to retrieve a path accordingly. Or if the context is unknown within actions (may/may not be root), you can use context.state._. However, this will require you to strictly hardcode the module interface field references (under each State for typehinting) if you're not using the builder utility. Also, a _ property reference must be set up as well per module state to ensure you get typehinting/type-completion. The Builder already sets up combined state for module references under a given parent state, so your Module state would probably just implement a dummy IPrefixedState interface that provides an optional readonly _?:string parameter that will give you boilerplate typehinting.

For strictly typed dispatches of module-specific mutations/actions, one way is to adopt a generic helper wrapper method to commit something through a given context (with/without namespace prefixing..)

https://github.com/Glidias/vuex-store-module-example/blob/master/src/util/vuexhelpers.ts

However, this will result in a bit of additional performance overhead of calling the wrapper helper function (remember, there's no inlining in Typescript). But i guess, it's okay and shouldn't be too much of an issue.

 import { MutationTypes } from './mutations';

import * as VuexHelper from "./util/vuexhelpers"
const commitTo = VuexHelper.getCommitToGeneric<MutationTypes>();

// within action handler context (affecting another module somewhere else..)
commitTo(context,"INC", "adults", {root:true}, context.rootState.someModule.anotherNestedModule._)

  // if a particular set of action handlers aren't aware if it's registered under a namespaced:true module or not
  commitTo(context,"INC", "adults", {root:true}, context.state._)

 // or something like this within a Component context:
commitTo(this.$store,"INC", "adults", undefined, $store.state.someModule.anotherNestedModule._)

Also, if your module needs to respond to both namespaced vs non-namespaced mutations/actions, namespaced:true vuex setting won't work well anyway. So, here's a possible approach in userland: https://github.com/Glidias/vuex-store-module-example/wiki/Managing-mixed-namespacings-between-module's-mutations-and-actions

@snaptopixel
Copy link
Contributor

I've done some work towards this end and the solution is coming along nicely. I've created a set of decorators similar to vue-class-component, which combine with a few conventions to make a pretty nice experience in TS. It's currently light on documentation (like, none heh) but the tests tell the story pretty well, I hope... https://github.com/snaptopixel/vuex-ts-decorators/blob/master/test/index.ts

I've invited @ktsn and @yyx990803 as collaborators but happy to receive ideas and pull requests from anyone. I'll be writing some docs and examples asap.

@ktsn
Copy link
Member

ktsn commented Feb 16, 2017

Thank you very much for all of your works!
I personally investigating this topic recently and created an experimental package of Vuex like store library. It actually does not Vuex but I believe the mechanism can help us to achieve Vuex's type safety somehow 🙂

@snaptopixel
Copy link
Contributor

snaptopixel commented Feb 16, 2017

Nice @ktsn I'll take a look at your work, thanks for the heads up! Give mine a look too as you find time.

@chanon
Copy link

chanon commented Feb 18, 2017

@snaptopixel I just tried out your decorators and I think they are pretty great!

Very simple to write and use. Defining stores isn't too different from normal Vuex and using them is exactly the same as normal Vuex except the added type safety (including with commit and dispatch!) which is nice!

However there are a few things that I could not get to work, so I added some questions/issues to your repository.

@snaptopixel
Copy link
Contributor

Awesome @chanon thank you so much for trying them out and filing issues. That helps tremendously. I'm planning on adding a proper readme soon and will definitely be working on the issues posted.

@istrib
Copy link

istrib commented May 6, 2017

My team was looking for a simple, tiny and unobtrusive solution. After several iterations I distilled it to this: https://github.com/istrib/vuex-typescript.
Using wrapper functions as a proxy to store.dispatch/commit/getters seemed most natural and higher-order functions with TypeScript type inference removed most boilerplate.
I specifically DID NOT want to use classes (there is nothing to encapsulate). Using ES6 modules to group actions/getters/mutations of the same Vuex module provides enough structure IMO.

//Vuex Module (like in JS + type annotations, no classes):

export const basket = {
    namespaced: true,

    mutations: {
        appendItem(state: BasketState, item: { product: Product; atTheEnd: boolean }) {
            state.items.push({ product: item.product, isSelected: false });
        },
    ...

//Strongly-typed wrappers:

const { commit } =
     getStoreAccessors<BasketState, RootState>("basket"); // Pass namespace here, if we make the module namespaced: true.

// commit is a higher-order function which gets handler function as argument
// and returns a strongly-typed "accessor" function which internally calls the standard store.commit() method.
// Implementation of commit is trivial: https://github.com/istrib/vuex-typescript/blob/master/src/index.ts#L103

// you get intellisense here:
export const commitAppendItem = commit(basket.mutations.appendItem);

// commitAppendItem is a function with strongly-typed signature
// so you get intellisense for function name and types of its arguments here too:

import * as basket from "./store/basket";
basket.commitAppendItem(this.$store, newItem);

@mrcrowl
Copy link

mrcrowl commented Jun 17, 2017

@istrib I really like what you've done in vuex-typescript.

I just wanted to take it a bit further—if you don't agree these ideas, that's totally okay. If you do agree, I'd be keen to incorporate these changes into vuex-typescript somehow.

My main changes are:

  • Avoid passing $store/context to the accessor methods: we can encapsulate these within the accessors by providing the store later:
    i.e. basket.commitAppendItem(newItem) should be sufficient.
  • No need to distinguish between payload / payload-less versions of commit + dispatch.
    Typescript overloads solve this problem.
  • Promises returned from dispatch should be strongly-typed.
  • Assumes namespaced modules

I also took the point of view that we don't need to start with a vuex-store options object. If we treat the accessor-creator as a builder, then the store can be generated:

import { getStoreBuilder } from "vuex-typex"
import Vuex, { Store, ActionContext } from "vuex"
import Vue from "vue"
const delay = (duration: number) => new Promise((c, e) => setTimeout(c, duration))

Vue.use(Vuex)

export interface RootState { basket: BasketState }
export interface BasketState { items: Item[] }
export interface Item { id: string, name: string }

const storeBuilder = getStoreBuilder<RootState>()
const moduleBuilder = storeBuilder.module<BasketState>("basket", { items: [] })

namespace basket
{
    const appendItemMutation = (state: BasketState, payload: { item: Item }) => state.items.push(payload.item)
    const delayedAppendAction = async (context: ActionContext<BasketState, RootState>) =>
    {
        await delay(1000)
        basket.commitAppendItem({ item: { id: "abc123", name: "ABC Item" } })
    }

    export const commitAppendItem = moduleBuilder.commit(appendItemMutation)
    export const dispatchDelayedAppend = moduleBuilder.dispatch(delayedAppendAction)
}
export default basket

/// in the main app file
const storeBuilder = getStoreBuilder<RootState>()
new Vue({
    el: '#app',
    template: "....",
    store: storeBuilder.vuexStore()
})

@Shepless
Copy link

What would be the recommended approach here? We are starting a new project at work and are really keen to have type safety on our stores (including commit/dispatch).

@istrib
Copy link

istrib commented Jun 20, 2017

I like your approach, @mrcrowl
I am successfully using a similar builder on one of my current projects. Works really well.

With https://github.com/istrib/vuex-typescript I searched for a solution which produces code that is identical to vanilla Vuex + a bit of simple stuff below it. For that reason I did not mind starting with Vuex options rather than have them built. That is also why I decided to explicitly pass $store into accessors. I like your variation for doing that much while still being tiny.

https://github.com/istrib/vuex-typescript now contains your two great suggestions: making promises returned from dispatch strongly-typed and using function overloads for mutations/actions without payload.

@Shepless
Copy link

@istrib do you have gist example for reference please?

@istrib
Copy link

istrib commented Jun 21, 2017

@Shepless There is a complete example here with this file giving the best overview.

@ghost
Copy link

ghost commented Aug 7, 2017

Thanks @istrib for the starting point. I am looking to get Vuex setup with typescript and I just stumbled across vuex-typescript. Unfortunately, I am struggling to get it working correctly. Is there a reference of how the store is actually instantiated?

I am doing:

import * as Vue from 'vue';
import * as Vuex from 'vuex';

// vuex store
import {createStore} from './store';

Vue.use(Vuex);

new Vue({
  el: '#app-main',
  store: createStore()
});

But I am getting the error:

[vuex] must call Vue.use(Vuex) before creating a store instance

My directory structure is pretty close to the example:
https://github.com/istrib/vuex-typescript/tree/master/src/tests/withModules/store

@DevoidCoding
Copy link

I've done an impl. of the two approach in a Todo app. Here's the repo Vue.js + Vuex (typescript & typex) • TodoMVC


@michaelharrisonroth You're certainly doing

export const createStore = new Vuex.Store<State>({

expect of

`export const createStore = () => new Vuex.Store<State>({`

in your store.ts file

@ghost
Copy link

ghost commented Aug 7, 2017

@DevoidCoding That was the issue and your TodoMVC is extremely helpful, thank you!

@F0rsaken
Copy link

F0rsaken commented Mar 16, 2020

So I made a small library wich allows you to get fully typed store in actions and components.
It also adds helpers that allow you to import rather than us mapXXX or decorators

https://github.com/owlsdepartment/vuex-typed

@gcollombet
Copy link

For me adding the store state type in module declaration is enough to have auto-completion

declare module "vue/types/vue" {
  interface Vue {
    $store: Store<RootState| any>;
  }
}

A more complete exemple :

import Vue from "vue"
import Vuex, { StoreOptions} from "vuex"
import App from './App.vue'

Vue.use(Vuex)

interface RootState {
  user: App.Models.User | null;
  notifications: App.Models.Notification[];
}

const store: StoreOptions<RootState> = {
  state: {
    user: null,
    notifications: []
  },
  mutations: {
    login(state, user: User) {
      state.user = user
    },
    logout(state) {
      state.user = null
    }
  },
  actions: {
  },
  modules: {
  }
}

store = new Vuex.Store<RootState>(store);

declare module "vue/types/vue" {
  interface Vue {
    // https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#use-union-types
    // eslint-disable-next-line @typescript-eslint/no-explicit-any
    $store: Store<RootState| any>;
  }
}

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app')

@sethidden
Copy link

sethidden commented Apr 25, 2020

The can't overwrite Store<any> issue (originally tracked in #994) will be resolved in Vuex 4 - beta version release notes here

@rfox12
Copy link

rfox12 commented May 19, 2020

Vuex has created a lot of pain for Typescript users. Now that we have the Composition API... do we even really need Vuex? In 2020, if I'm training a team to use Vue 3, why would I teach them Vuex? Creating stateful singletons that you can compose is a really nice pattern. Code is so much cleaner without Vuex.

The arguments in favor of Vuex (in my mind) are:

  • Vue devTools integration (time travel, etc.)
  • Slightly easier to avoid name collisions with namespace modules?

What am I missing?

@yoyoys
Copy link

yoyoys commented May 19, 2020

Vuex has created a lot of pain for Typescript users. Now that we have the Composition API... do we even really need Vuex? In 2020, if I'm training a team to use Vue 3, why would I teach them Vuex? Creating stateful singletons that you can compose is a really nice pattern. Code is so much cleaner without Vuex.

The arguments in favor of Vuex (in my mind) are:

  • Vue devTools integration (time travel, etc.)
  • Slightly easier to avoid name collisions with namespace modules?

What am I missing?

Agree, I've trained my teammate to use Vue's injection to provide shared data between components.

@garyo
Copy link

garyo commented May 19, 2020

This is true, but Vue dev tools integration is not a small thing. It can be extremely useful for debugging.

@tonnyorg
Copy link

I'm kinda facing the same issue here, this is how my actions.ts looks like:

export default {
  updateIp(context: any, ip: string) {
    context.commit('setIp', ip);
  }
}

But I get this warning:

  2:21  warning  Unexpected any. Specify a different type  @typescript-eslint/no-explicit-any

Any idea how to fix this?

@rfox12
Copy link

rfox12 commented May 20, 2020

A shout out to @paleo, direct-vuex is fantastic. Strongly typed coding within the store and (perhaps more importantly) when components access the store state and actions.

I tried Vuex 4 today, but it didn't really do much for me (I'm still on Vue2 + Composition add-on). Is there an outline of how Vuex 4 will work for Typescript users? All I could find was an old roadmap with generic statements about "supporting Typescript"

@sethidden
Copy link

sethidden commented May 20, 2020

@rfox12 As far as I know, you'll need to wait for Vuex 5. Vuex 4 was focused on preparing for Vue 3 and allowing passing your own interface to Store<>

Before Vuex 4, you'd have to do:

new Vue({
  store: new Store(store) as Store<any>
})

thus this.$store.state wouldn't be typed. Working around that (passing your own store interface instead of any) required some trickery

@rfox12
Copy link

rfox12 commented May 30, 2020

I'll add two additional compelling reasons to keep Vuex in Composition API world:

  • Support for Hot module reloading
  • The string descriptors can be useful if you need to trigger actions from a server (e.g. sockets)

So in short... I'm keeping Vuex for large projects, but I have an question. First some background. To get typescript support today I find it best to import store from './store'; and then use something like store.dispatch.core.signOut() (with direct-vuex). Inside my single-page components. This is instead of using this.$store (which I cannot get Typescript to understand yet--still on Vuex 3). My question is: is there anything magical about Vue's instance of the store that I should be aware of? Any wrapping or logic that would make this.$store different from the imported version?

@kiaking
Copy link
Member

kiaking commented Jun 1, 2020

@rfox12 You should always use this.$store if you're doing SSR. Otherwise, it doesn't matter.

The difference with direct import is that when you use this.$store, you're using the store instance injected into the Vue instance. When doing SSR, at server side, if you use direct import, the store instance will be shared between the different requests because the store instance becomes a global singleton. You might get state pollution.

If you're not doing SSR, it's fine to directly import the store since it will be used by only one client (in browser).

@MikeMitterer
Copy link

@Teebo Here is my solution: https://github.com/MikeMitterer/vue-ts-starter/tree/master/src/store

@Teebo
Copy link

Teebo commented Aug 8, 2020

@MikeMitterer thank you so much, I will go through the repo to understand the setup, but from a glance, it looks good, thank you!

@Teebo
Copy link

Teebo commented Aug 8, 2020

@MikeMitterer, in the file https://github.com/MikeMitterer/vue-ts-starter/blob/master/src/store/utils/index.ts
The isNotRegistered func:
Is the check mainly for checking if the child store module is registered or it also checks the existence of the RootStore and if it has a state?
I am just wondering if the built-in hasModule could be used.

I see that in the actions return promises in the counter store, is the a requirement from vuex-module-decorators or it is just a usecase example?

@RandomErrorMessage
Copy link

Been subscribed to this issue for like 2 years, the absolute state of webdev. Glad I dropped this stuff, yikes.

@ChanningHan
Copy link

It is v4.0.0-rc.2 now.
But it seems to haven't no TypeScript supporting for Mutations and Actions.
Why do the Mutations or Actions have to be used by giving identifier string to trigger?
So, will there be any new feature or API to implement type suporting for mutations and actions?

@ClickerMonkey
Copy link

@ChanningHan I've been working on full type support for vuex in my personal time, and with TS 4.1 it seems definitely possible. The only thing holding it back is a problem with TS where it obnoxiously warns about "potentially infinite blah blah". I will be updating this issue: #1831

@ChanningHan
Copy link

@ClickerMonkey Bravo~It will be exciting!
ActuaIly, I used to be a heavy user of Vuex before using TS. So, I really hope to see the full type support for vuex, and thank you for your hard working on it♥

@tarkhil
Copy link

tarkhil commented Jan 4, 2021

Spent several hours trying to modularize vuex store with TypeScript, I feel TS is just a way to write your code two times more. I do understand the importance of type safety, but I gave up trying to describe all types...

@charles-allen
Copy link

Spent several hours trying to modularize vuex store with TypeScript, I feel TS is just a way to write your code two times more. I do understand the importance of type safety, but I gave up trying to describe all types...

TypeScript works best when you just describe a few core domain types & the rest of your code has types inferred from how you operated on those core types & library types. It's especially important for libraries/frameworks to export APIs with types; a few anys sneaking in from libraries creates a lots of manual typing overhead.

@francoism90
Copy link

How should you write getters? I'm using this example: https://next.vuex.vuejs.org/guide/typescript-support.html#simplifying-usestore-usage

Doing store.getters['foo/bar'] doesn't work because it returns 'any', how do you type hint these?

@sethidden
Copy link

sethidden commented Feb 19, 2021

You can't. Its the same as in Vue 2. The newest version of Vuex is mainly for Vue 3 support. Wait for Vuex 5 or use vuex-module-decorators (or similar pkgs).

There's also an issue in this repo that proposes using TS 4.1 template literal types for typing

@francoism90

@francoism90
Copy link

@3nuc I'm new to TypeScript so don't know what any of that means lol. As a workaround I'll be sticking to mapGetters as TS doesn't complain about this. State injection seems to working, but using helpers defeats the point of TS I think.

Will take a look at vuex-module-decorators, although I'll probably wait for Vuex 5. Thanks!

@usernamehw
Copy link

https://github.com/posva/pinia - looks pretty good. Works with TypeScript out of the box.

@francoism90
Copy link

@usernamehw Yeah, I also using Pinia as TS replacement. :)

@championswimmer
Copy link

People who prefer classical OOP-based concepts and not too functional looking code, can check out my library

https://github.com/championswimmer/vuex-module-decorators

@kiaking kiaking removed the discussion label Oct 4, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
types Related to typings only
Projects
None yet
Development

Successfully merging a pull request may close this issue.