vuejs / vuex

🗃️ Centralized State Management for Vue.js.
https://vuex.vuejs.org
MIT License
28.4k stars 9.56k forks source link

Plans for complete TypeScript support with TS 4.1.0 #1831

Open ClickerMonkey opened 4 years ago

ClickerMonkey commented 4 years ago

What problem does this feature solve?

With the introduction of inference from template strings in TS 4.1.0 it will finally be possible to make Vuex completely type safe (validating all types, paths for commit/dispatch/modules/getters, etc).

Are there existing plans to add complete type support with the release of Vuex 4?

For reference, I've created the following project:

https://github.com/ClickerMonkey/vuex-typescript-interface

The only adjustment to the existing type system is that it would require an interface for each module and the root state - so that the modules and root state are aware of each others types. It could be optional like it currently is.

Here's how the new TS feature would solve the remaining challenge with complete type support with Vuex:

type GetEmbeddedType<O, Path extends string> = 
    Path extends `${infer A}/${infer B}` 
    ? A extends keyof O
        ? B extends keyof O[A]
            ? O[A][B]
            : never
        : never
    : never;

interface State {
    prop: {
        innerProp: number;
    }
};

type InnerPropType = GetEmbeddedType<State, 'prop/innerProp'>; // number!!

What does the proposed API look like?

Similar to what exists now, just full type support to validate types, the existence of all mutations and dispatches, and communications between the root store and it' modules.

kiaking commented 3 years ago

Yes! We're aware of TS 4.1 and this might work. To be honest I was thinking to focus TS on Vuex 5 but this kinda did change things 😅

Let's come back to this issue once TS 4.1 is released, and see how it goes. PR is welcome too! I think there's no harm trying out this feature 👍

Also, I think this can be added to both Vuex 3 & 4...?

savannahp commented 3 years ago

This would realllyyy help! Instead have to use type any just to get it to compile when it comes to your store.ts file

timritzer commented 3 years ago

I have written types to support 95% of the Vuex functionality. Because of some of the API surface I went with a wrapper approach, but the API surface is virtually identical. It supports all the mapper functions, and allows fully typed, checked access including namespaces. Would love some feedback if anyone finds it useful.

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

It requires TS 4.1 so that the template literal functionality is there, and works best with strict on.

mohammedzamakhan commented 3 years ago

Any updates for better Typescript support for Vuex 4?

glen-84 commented 3 years ago

Does anyone know how to augment the vuex types to achieve what is shown here:

type VuexOptions<M, N> = {
   namespace?: N,
   mutations: M,
}

type Action<M, N> = N extends string ? `${N}/${keyof M & string}` : keyof M

type Store<M, N> = {
   dispatch(action: Action<M, N>): void
}

declare function Vuex<M, N>(options: VuexOptions<M, N>): Store<M, N>

const store = Vuex({
   namespace: "cart" as const,
   mutations: {
      add() { },
      remove() { }
   }
})

store.dispatch("cart/add")
store.dispatch("cart/remove")

image

(playground)

Edit: See also the 2nd article Realize Vuex unlimited level type inference (TS 4.1 new feature), which shows support for nested modules.

Shinigami92 commented 3 years ago

Isn't is possible to get something like

store.cart.add.dispatch()
// or
store.cart.mutations.add.dispatch()
// or
store.cart.mutations.add()

This would be a whole new feature request, but would increase the DX by far IMO

glen-84 commented 3 years ago

@Shinigami92 There's direct-vuex, but it seems a bit awkward. This should be part of vuex itself IMO.

Shinigami92 commented 3 years ago

@glen-84 Need to test this out next week. Thanks for pointing to this, didn't know it.

timritzer commented 3 years ago

Does anyone know how to augment the vuex types to achieve what is shown here:

type VuexOptions<M, N> = {
   namespace?: N,
   mutations: M,
}

type Action<M, N> = N extends string ? `${N}/${keyof M & string}` : keyof M

type Store<M, N> = {
   dispatch(action: Action<M, N>): void
}

declare function Vuex<M, N>(options: VuexOptions<M, N>): Store<M, N>

const store = Vuex({
   namespace: "cart" as const,
   mutations: {
      add() { },
      remove() { }
   }
})

store.dispatch("cart/add")
store.dispatch("cart/remove")

image

(playground)

Edit: See also the 2nd article Realize Vuex unlimited level type inference (TS 4.1 new feature), which shows support for nested modules.

Ya, absolutely. We use it this way. I'll try to distill it down for you. I use the above package to accomplish it.

We use modules, so I can't give you examples without modules, but the approach would be similar.

I'll make a small Gist.

timritzer commented 3 years ago

@glen-84 Here is a sample of how I am doing it: https://github.com/timritzer/typed-vuex-examples Feel free to ask any questions. It is working great for us, as we are any-free and have full types and auto-complete

glen-84 commented 3 years ago

@timritzer,

Thanks for putting that together. However, I was looking for something that would not require a separate package. I was hoping to be able to augment the existing types to make them enforce the use of specific strings (as derived from the actions, mutations, etc. in each module/sub-module).

timritzer commented 3 years ago

@glen-84 I mean the only package is a set of types I developed to make everything strongly typed. It's a pretty tiny package, handful of TS files, and no real functionality at run time. It has a few passthrough methods to use inferring the types, but that is it.

Feel free to copy the files and use it not as a package, I don't mind a bit. I just made it a package to follow the pattern everyone uses and make it easy to add and update.

kadet1090 commented 3 years ago

I've made my own type system that aims to be as non-intrusive as it's possible: https://github.com/kadet1090/vuex-typings for now it's not available as any JS package - I'm looking forward to do so, but I don't want for anyone to depend on something that in the end would not work out.

It supports namespaced and global modules, nested modules and has a lot of helpers - everything should be clearly described in the readme.

Grawl commented 3 years ago

@kadet1090 looks cool! please follow this project to finish, can't wait to use it in my projects

miaulightouch commented 3 years ago

I had wait typing gonna be improved for about one year, there are few related PRs have no chance to be merged.

I'm considering alternatives like pinia or harlem.

kadet1090 commented 3 years ago

I think that there are two issues for creating better typings for vuex:

I potentially could finish my proposal of types and try to get it merged into core but this seems unlikely, as I'd need some feedback from the maintainers.