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

vuex use TS 4.1 Template Literal Types #2013

Closed
mioxs opened this issue Jun 26, 2021 · 1 comment
Closed

vuex use TS 4.1 Template Literal Types #2013

mioxs opened this issue Jun 26, 2021 · 1 comment

Comments

@mioxs
Copy link

mioxs commented Jun 26, 2021

What problem does this feature solve?

image

image

image

What does the proposed API look like?

vuex.ts
Template Literal Types Help class, no intrusion config

import {
  CommitOptions,
  DispatchOptions,
  Store as VuexStore,
  useStore as vuexUseStore,
  createStore as vuexCreateStore,
  StoreOptions as VuexStoreOptions,
  ActionContext as VuexActionContext,
} from 'vuex';

export type LiteralType<Prefix, Keys> = `${Prefix & string}/${keyof Keys &
  string}`;

//modules first level node
export type GetMutations<T> = T extends { mutations: infer V } ? V : never;
export type GetActions<T> = T extends { actions: infer V } ? V : never;
export type GetGetters<T> = T extends { getters: infer V } ? V : never;
export type GetModules<T> = T extends { modules: infer V } ? V : never;
export type GetState<T> = T extends { state: infer V } ? V : never;

//mutations subModules child node
export type SubModuleMutationsKeys<Module, Key> = Module extends {
  modules: infer SubModules;
}
  ? LiteralType<Key, ModulesMutationsKeys<SubModules>>
  : never;

export type ModuleMutationsKeys<Module, Key> =
  | LiteralType<Key, GetMutations<Module>>
  | SubModuleMutationsKeys<Module, Key>;

export type ModulesMutationsKeys<Modules> = {
  [K in keyof Modules]: ModuleMutationsKeys<Modules[K], K>;
}[keyof Modules];

//actions subModules child node
export type SubModuleActionsKeys<Module, Key> = Module extends {
  modules: infer SubModules;
}
  ? LiteralType<Key, ModulesActionsKeys<SubModules>>
  : never;

export type ModuleActionsKeys<Module, Key> =
  | LiteralType<Key, GetActions<Module>>
  | SubModuleActionsKeys<Module, Key>;

export type ModulesActionsKeys<Modules> = {
  [K in keyof Modules]: ModuleActionsKeys<Modules[K], K>;
}[keyof Modules];

//getters subModules child node
export type SubModuleGettersKeys<Module, Key> = Module extends {
  modules: infer SubModules;
}
  ? LiteralType<Key, ModulesGettersKeys<SubModules>>
  : never;

export type ModuleGettersKeys<Module, Key> =
  | LiteralType<Key, GetGetters<Module>>
  | SubModuleGettersKeys<Module, Key>;

export type ModulesGettersKeys<Modules> = {
  [K in keyof Modules]: ModuleGettersKeys<Modules[K], K>;
}[keyof Modules];

//state subModules child node
export type SubModuleState<Module> = Module extends {
  modules: infer SubModules;
}
  ? ModulesState<SubModules>
  : never;

export type ModuleState<Module> = GetState<Module> | SubModuleState<Module>;

export type ModulesState<Modules> = {
  [K in keyof Modules]: ModuleState<Modules[K]>;
};

//get Keys
export type Commit<Mutations, Modules> =
  | keyof Mutations
  | ModulesMutationsKeys<Modules>;

export type Dispatch<Actions, Modules> =
  | keyof Actions
  | ModulesActionsKeys<Modules>;

export type Getter<G, Modules> = keyof G | ModulesGettersKeys<Modules>;

type RootState<T> = T extends unknown ? Record<string, unknown> : T;

export type States<State, Modules> = RootState<State> & ModulesState<Modules>;

interface LiteralStore<State, Mutations, Actions, Getters, Modules>
  extends Omit<
    VuexStoreOptions<any>,
    'state' | 'getters' | 'mutations' | 'actions' | 'modules'
  > {
  state?: State;
  getters?: Getters;
  mutations?: Mutations;
  actions?: Actions;
  modules?: Modules;
}

export interface StoreOptions<
  S = unknown,
  Mutations = unknown,
  Actions = unknown,
  Getters = unknown,
  Modules = unknown,
> extends LiteralStore<S, Mutations, Actions, Getters, Modules> {}

interface Store<S, Mutations, Actions, Getters, Modules>
  extends LiteralStore<S, Mutations, Actions, Getters, Modules>,
    Omit<VuexStore<any>, 'state' | 'getters'> {}

export function createStore<S, Mutations, Actions, Getters, Modules>(
  options: StoreOptions<S, Mutations, Actions, Getters, Modules>,
): Store<S, Mutations, Actions, Getters, Modules> {
  return vuexCreateStore(<VuexStoreOptions<any>>options);
}

export class UseStore<S extends StoreOptions> {
  private readonly store: VuexStore<any>;

  readonly state: States<GetState<Required<S>>, GetModules<Required<S>>>;

  readonly getters: Record<
    Getter<GetGetters<Required<S>>, GetModules<Required<S>>>,
    any
  >;

  constructor() {
    this.store = vuexUseStore();
    this.state = this.store.state;
    this.getters = this.store.getters;
  }

  commit<P = any>(
    type: Commit<GetMutations<Required<S>>, GetModules<Required<S>>>,
    payload?: P,
    options?: CommitOptions,
  ) {
    this.store.commit(type as string, payload, options);
  }

  async dispatch<P = any>(
    type: Dispatch<GetActions<Required<S>>, GetModules<Required<S>>>,
    payload?: P,
    options?: DispatchOptions,
  ) {
    await this.store.dispatch(type as string, payload, options);
  }
}

export function useStore<Vuex>() {
  return new UseStore<Vuex>();
}

export interface ActionContext<T>
  extends Omit<VuexActionContext<any, any>, 'commit'> {
  commit<P = any>(type: keyof T, payload?: P, options?: CommitOptions): void;
}

Example

demo.ts

import { ActionContext } from './vuex';

interface State {
  width: number;
}
const mutations = {
  screenWidth(state: State, width: number) {
    state.width = width;
  },
};
export default {
  namespaced: true,
  state: {
    width: 0,
  },
  mutations,
  actions: {
    test({ commit }: ActionContext<typeof mutations>) {
      commit('screenWidth');
    },
  },
};

store.ts

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

const store = createStore({
  state: {
    test:1
  },
  modules: {
    demo 
  }
});

export default store;

export type Vuex = typeof store;

main.ts

import { createApp } from 'vue';

import vuex from './store';
import App from './App';

const app = createApp(App).use(vuex);

Use

import { defineComponent, watch } from 'vue';
import { useStore } from './vuex';
import { Vuex } from './store';

export default defineComponent({
  name: 'App',
  setup() {
    const store = useStore<Vuex>();
    const value = store.state.demo.width;
     store.commit('demo/screenWidth',Math.random().toFixed(3));
    return () => {
      return (
        <div>
          <button
            onClick={() => {
              store.commit('demo/screenWidth',Math.random().toFixed(6));
            }}>
            test
          </button>
        </div>
      );
    };
  },
});

#1831 (comment)

@ue360
Copy link

ue360 commented Aug 2, 2021

getters?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants