Skip to content

Instantly share code, notes, and snippets.

@romansp
Created January 8, 2019 12:50
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save romansp/72e0019e0e02b27da92b378922b67940 to your computer and use it in GitHub Desktop.
Save romansp/72e0019e0e02b27da92b378922b67940 to your computer and use it in GitHub Desktop.
Type-safe vuex payloads
import { Payload } from "vuex";
import { PayloadMap, ActionTree } from "./vuex-typed";
import { State } from "./state";
import { Mutation } from "./mutations";
// define action types by adding new values to this enum
export enum ActionTypes {
Action = "Action",
}
// add mutation payload class
export class Action implements Payload {
public readonly type = ActionTypes.Action;
constructor(public readonly actionPayload: string) { }
}
// defining a map between action key and action payload
// PayloadMap enforces action `type` field value and key value to be equal
// otherwise it will throw compile-time error
export interface ActionPayloadMap extends PayloadMap<ActionTypes> {
[ActionTypes.Action]: Action;
}
const actions: ActionTree<State, State, ActionPayloadMap> = {
// action payload type is inferred from action type
async [ActionTypes.Action]({ commit }, { actionPayload }) {
commit(new Mutation(actionPayload))
},
}
export default actions;
import store from "./store";
import { Action } from "./store/actions";
import { Mutation } from "./store/mutations";
// when dispatching actions and commiting mutations payload is typed
store.dispatch(new Action("hello"));
store.commit(new Mutation("hello"));
// compilation error: Argument of type '1' is not assignable to parameter of type 'string'
store.commit(new Mutation(1));
// compilation error: Expected 1 arguments, but got 0.
store.commit(new Mutation());
import { Payload } from "vuex";
import { PayloadMap, MutationTree } from "./vuex-typed";
import { State } from "./state";
// define mutation types by adding new values to this enum
export enum MutationTypes {
Mutation = "Mutation",
}
// add mutation payload classes
export class Mutation implements Payload {
public readonly type = MutationTypes.Mutation;
constructor(public readonly mutationPayload: string) { }
}
// defining a map between mutation key and mutaion payload
// PayloadMap enforces mutation `type` field value and key value to be equal
// otherwise it will throw compile-time error
export interface MutationPayloadMap extends PayloadMap<MutationTypes> {
[MutationTypes.Mutation]: Mutation;
}
const mutations: MutationTree<State, MutationPayloadMap> = {
// mutation payload type is inferred from mutation type
async [MutationTypes.Mutation](state, { mutationPayload }) {
state.value = mutationPayload;
},
}
export default mutations;
export interface State {
value: string;
}
const state: State = {
value: ""
};
export default state;
import Vuex from "vuex";
import actions from "./actions";
import mutations from "./mutations";
import state from "./state";
export default new Vuex.Store({
state,
actions,
mutations,
});
import { ActionContext } from "vuex";
type ActionHandler<S, R, P> = (injectee: ActionContext<S, R>, payload: P) => any;
interface ActionObject<S, R, P> {
root?: boolean;
handler: ActionHandler<S, R, P>;
}
type Mutation<S, P = any> = ((state: S, payload: P) => any);
type Action<S, R, P> = ActionHandler<S, R, P> | ActionObject<S, R, P>;
export type MutationTree<S, M = any> = { [P in keyof M]: Mutation<S, M[P]> };
export type ActionTree<S, R, M = any> = { [P in keyof M]: Action<S, R, M[P]> };
export interface Payload<T = string> {
readonly type: T;
}
export type PayloadMap<T extends keyof any> = { [P in T]: Payload<P> };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment