ソースを参照

TypeScript declarations for 2.0 (#301)

* Modify type files to external module definition format to publish them via npm (#242)

* Rewrite d.ts in external module definition format

* Publish type files via npm

* update d.ts for 2.0

* update d.ts tests for 2.0

* provide helpers.d.ts via npm
katashin 8 年 前
コミット
8cd5a708c9
9 ファイル変更396 行追加223 行削除
  1. 14 0
      logger.d.ts
  2. 6 1
      package.json
  3. 18 0
      types/helpers.d.ts
  4. 86 75
      types/index.d.ts
  5. 47 0
      types/test/helpers.ts
  6. 185 145
      types/test/index.ts
  7. 13 1
      types/test/tsconfig.json
  8. 10 1
      types/tsconfig.json
  9. 17 0
      types/vue.d.ts

+ 14 - 0
logger.d.ts

@@ -0,0 +1,14 @@
+/**
+ * Types for the logger plugin.
+ * This file must be put alongside the JavaScript file of the logger.
+ */
+
+import { Payload, Plugin } from './types/index'
+
+export interface LoggerOption<S> {
+  collapsed?: boolean;
+  transformer?: (state: S) => any;
+  mutationTransformer?: <P extends Payload>(mutation: P) => any;
+}
+
+export default function createLogger<S>(option: LoggerOption<S>): Plugin<S>;

+ 6 - 1
package.json

@@ -3,10 +3,15 @@
   "version": "2.0.0-rc.5",
   "description": "state management for Vue.js",
   "main": "dist/vuex.js",
+  "typings": "types/index.d.ts",
   "files": [
     "dist",
     "src",
-    "logger.js"
+    "types/index.d.ts",
+    "types/helpers.d.ts",
+    "types/vue.d.ts",
+    "logger.js",
+    "logger.d.ts"
   ],
   "scripts": {
     "dev": "node examples/server.js",

+ 18 - 0
types/helpers.d.ts

@@ -0,0 +1,18 @@
+type Dictionary<T> = { [key: string]: T };
+
+export function mapState (map: string[]): Dictionary<() => any>;
+export function mapState (map: Dictionary<string>): Dictionary<() => any>;
+export function mapState <S>(
+  map: Dictionary<(this: vuejs.Vue, state: S, getters: any) => any>
+): Dictionary<() => any>;
+
+type MutationMethod = (...args: any[]) => void;
+export function mapMutations (map: string[]): Dictionary<MutationMethod>;
+export function mapMutations (map: Dictionary<string>): Dictionary<MutationMethod>;
+
+export function mapGetters (map: string[]): Dictionary<() => any>;
+export function mapGetters (map: Dictionary<string>): Dictionary<() => any>;
+
+type ActionMethod = (...args: any[]) => Promise<any[]>;
+export function mapActions (map: string[]): Dictionary<ActionMethod>;
+export function mapActions (map: Dictionary<string>): Dictionary<ActionMethod>;

+ 86 - 75
types/index.d.ts

@@ -1,92 +1,103 @@
-declare namespace Vuex {
-  class Store<S> {
-    constructor(options: StoreOption<S>);
+import "./vue";
 
-    state: S;
+export * from "./helpers";
 
-    dispatch(mutationName: string, ...args: any[]): void;
-    dispatch<P>(mutation: MutationObject<P>): void;
+export declare class Store<S> {
+  constructor(options: StoreOptions<S>);
 
-    replaceState(state: S): void;
+  readonly state: S;
+  readonly getters: any;
 
-    watch<T>(getter: Getter<S, T>, cb: (value: T) => void, options?: WatchOption): void;
+  replaceState(state: S): void;
 
-    hotUpdate(options: {
-      mutations?: MutationTree<S>;
-      modules?: ModuleTree;
-    }): void;
+  dispatch: Dispatch;
+  commit: Commit;
 
-    subscribe(cb: (mutation: MutationObject<any>, state: S) => void): () => void;
-  }
+  subscribe<P extends Payload>(fn: (mutation: P, state: S) => any): () => void;
+  watch<T>(getter: (state: S) => T, cb: (value: T) => void, options?: WatchOption): void;
 
-  function install(Vue: vuejs.VueStatic): void;
+  registerModule<T>(path: string, module: Module<T, S>): void;
+  registerModule<T>(path: string[], module: Module<T, S>): void;
 
-  interface StoreOption<S> {
-    state?: S;
+  unregisterModule(path: string): void;
+  unregisterModule(path: string[]): void;
+
+  hotUpdate(options: {
+    actions?: ActionTree<S, S>;
     mutations?: MutationTree<S>;
-    modules?: ModuleTree;
-    plugins?: Plugin<S>[];
-    strict?: boolean;
-  }
-
-  type Getter<S, T> = (state: S) => T;
-  type Action<S> = (store: Store<S>, ...args: any[]) => any;
-  type Mutation<S> = (state: S, ...args: any[]) => void;
-  type Plugin<S> = (store: Store<S>) => void;
-
-  interface MutationTree<S> {
-    [key: string]: Mutation<S>;
-  }
-
-  interface MutationObject<P> {
-    type: string;
-    silent?: boolean;
-    payload?: P;
-  }
-
-  interface Module<S> {
-    state: S;
-    mutations: MutationTree<S>;
-  }
-
-  interface ModuleTree {
-    [key: string]: Module<any>;
-  }
-
-  interface ComponentOption<S> {
-    getters: { [key: string]: Getter<S, any> };
-    actions: { [key: string]: Action<S> };
-  }
-
-  interface WatchOption {
-    deep?: boolean;
-    immidiate?: boolean;
-  }
-
-  function createLogger<S>(option: LoggerOption<S>): Plugin<S>;
-
-  interface LoggerOption<S> {
-    collapsed?: boolean;
-    transformer?: (state: S) => any;
-    mutationTransformer?: (mutation: MutationObject<any>) => any;
-  }
+    getters?: GetterTree<S, S>;
+    modules?: ModuleTree<S>;
+  }): void;
+}
+
+export declare function install(Vue: vuejs.VueStatic): void;
+
+export interface Dispatch {
+  (type: string, payload?: any): Promise<any[]>;
+  <P extends Payload>(payloadWithType: P): Promise<any[]>;
+}
+
+export interface Commit {
+  (type: string, payload?: any, options?: CommitOptions): void;
+  <P extends Payload>(payloadWithType: P, options?: CommitOptions): void;
+}
+
+export interface ActionInjectee<S, R> {
+  dispatch: Dispatch;
+  commit: Commit;
+  state: S;
+  getters: any;
+  rootState: R;
+}
+
+export interface Payload {
+  type: string;
 }
 
-declare namespace vuejs {
-  interface ComponentOption {
-    vuex?: Vuex.ComponentOption<any>;
-    store?: Vuex.Store<any>;
-  }
+export interface CommitOptions {
+  silent?: boolean;
+}
+
+export interface StoreOptions<S> {
+  state?: S;
+  getters?: GetterTree<S, S>;
+  actions?: ActionTree<S, S>;
+  mutations?: MutationTree<S>;
+  modules?: ModuleTree<S>;
+  plugins?: Plugin<S>[];
+  strict?: boolean;
+}
+
+export type Getter<S, R> = (state: S, getters: any, rootState: R) => any;
+export type Action<S, R> = (injectee: ActionInjectee<S, R>, payload: any) => any;
+export type Mutation<S> = (state: S, payload: any) => any;
+export type Plugin<S> = (store: Store<S>) => any;
+
+export interface Module<S, R> {
+  state?: S;
+  getters?: GetterTree<S, R>;
+  actions?: ActionTree<S, R>;
+  mutations?: MutationTree<S>;
+  modules?: ModuleTree<R>;
+}
+
+export interface GetterTree<S, R> {
+  [key: string]: Getter<S, R>;
+}
+
+export interface ActionTree<S, R> {
+  [key: string]: Action<S, R>;
+}
 
-  interface Vue {
-    $store?: Vuex.Store<any>;
-  }
+export interface MutationTree<S> {
+  [key: string]: Mutation<S>;
 }
 
-declare module 'vuex' {
-  export = Vuex
+export interface ModuleTree<R> {
+  [key: string]: Module<any, R>;
 }
 
-declare module 'vuex/logger' {
-  export default Vuex.createLogger;
+export interface WatchOption {
+  deep?: boolean;
+  immediate?: boolean;
 }

+ 47 - 0
types/test/helpers.ts

@@ -0,0 +1,47 @@
+import * as Vue from "vue";
+
+import {
+  mapState,
+  mapGetters,
+  mapActions,
+  mapMutations
+} from "../index";
+
+new Vue({
+  computed: Object.assign({},
+    mapState(["a"]),
+    mapState({
+      b: "b"
+    }),
+    mapState({
+      c: (state: any, getters: any) => state.c + getters.c
+    }),
+
+    mapGetters(["d"]),
+    mapGetters({
+      e: "e"
+    }),
+
+    {
+      otherComputed () {
+        return "f";
+      }
+    }
+  ),
+
+  methods: Object.assign({},
+    mapActions(["g"]),
+    mapActions({
+      h: "h"
+    }),
+
+    mapMutations(["i"]),
+    mapMutations({
+      j: "j"
+    }),
+
+    {
+      otherMethod () {}
+    }
+  )
+});

+ 185 - 145
types/test/index.ts

@@ -1,194 +1,234 @@
-import * as Vue from 'vue';
-import * as Vuex from 'vuex';
-import createLogger from 'vuex/logger';
+import * as Vue from "vue";
+import * as Vuex from "../index";
+import createLogger from "../../logger";
 
 Vue.use(Vuex);
 
-interface ISimpleState {
-  count: number;
-}
+namespace StoreInstance {
+  const store = new Vuex.Store({
+    state: {
+      value: 0
+    }
+  });
 
-const INCREMENT = 'INCREMENT';
-const INCREMENT_OBJECT = 'INCREMENT_OBJECT';
+  store.state.value;
+  store.getters.foo;
 
-function createStore(): Vuex.Store<ISimpleState> {
-  const state: ISimpleState = {
-    count: 0
-  };
+  store.dispatch("foo", { amount: 1 }).then(() => {});
+  store.dispatch({
+    type: "foo",
+    amount: 1
+  }).then(() => {});
+
+  store.commit("foo", { amount: 1 }, { silent: true });
+  store.commit({
+    type: "foo",
+    amount: 1
+  }, { silent: true });
+
+  store.watch(state => state.value, value => {
+    value = value + 1;
+  }, {
+    immediate: true,
+    deep: true
+  });
 
-  const mutations: Vuex.MutationTree<ISimpleState> = {
-    [INCREMENT] (state: ISimpleState, amount: number) {
-      state.count = state.count + amount;
-    },
-    [INCREMENT_OBJECT] (state: ISimpleState, payload: number) {
-      state.count = state.count + payload;
-    }
-  };
+  store.subscribe((mutation, state) => {
+    mutation.type;
+    state.value;
+  });
 
-  return new Vuex.Store({
-    state,
-    mutations,
+  store.replaceState({ value: 10 });
+}
+
+namespace RootModule {
+  const store = new Vuex.Store({
+    state: {
+      value: 0
+    },
+    getters: {
+      count: state => state.value,
+      plus10: (_, { count }) => count + 10
+    },
+    actions: {
+      foo ({ state, getters, dispatch, commit }, payload) {
+        state.value;
+        getters.count;
+        dispatch("bar", {});
+        commit("bar", {}, { silent: true });
+      }
+    },
+    mutations: {
+      bar (state, payload) {}
+    },
     strict: true
   });
 }
 
-namespace TestDispatch {
-  const store = createStore();
+namespace NestedModules {
+  interface RootState {
+    a: {
+      value: number;
+    };
+    b: {
+      c: {
+        value: number;
+      };
+      d: {
+        value: number;
+      };
+    };
+  }
 
-  store.dispatch(INCREMENT, 1);
-  store.dispatch({
-    type: INCREMENT_OBJECT,
-    silent: true,
-    payload: 10
-  });
-}
+  type ActionStore = Vuex.ActionInjectee<{ value: number }, RootState>
 
-namespace TestWithComponent {
-  const store = createStore();
-
-  const a: vuejs.ComponentOption = {
-    vuex: {
-      getters: {
-        count: (state: ISimpleState) => state.count
-      },
-      actions: {
-        incrementCounter({ dispatch, state }: Vuex.Store<ISimpleState>) {
-          dispatch(INCREMENT, 1);
-        }
+  const module = {
+    state: {
+      value: 0
+    },
+    actions: {
+      foo (
+        { state, getters, dispatch, commit, rootState }: ActionStore,
+        payload: { amount: number }
+      ) {
+        state.value;
+        getters.root;
+        rootState.b.c.value;
+        dispatch("bar", {});
+        commit("bar", payload);
+      }
+    },
+    mutations: {
+      bar (state: { value: number }, payload: { amount: number }) {
+        state.value += payload.amount;
       }
     }
   };
 
-  const app = new Vue({
-    el: '#app',
-    components: { a },
-    store
+  const store = new Vuex.Store<RootState>({
+    getters: {
+      root: state => state
+    },
+    modules: {
+      a: module,
+      b: {
+        modules: {
+          c: module,
+          d: module
+        }
+      }
+    }
   });
-
-  const b: number = app.$store.state.count;
 }
 
-namespace TestModules {
-  interface IModuleAState {
+namespace RegisterModule {
+  interface RootState {
     value: number;
+    a?: {
+      value: number;
+      b?: {
+        value: number;
+      }
+    };
   }
 
-  interface IModuleBState {
-    value: string;
-  }
-
-  interface IModuleState {
-    a: IModuleAState;
-    b: IModuleBState;
-  }
-
-  const aState: IModuleAState = { value: 1 };
-  const bState: IModuleBState = { value: 'test' };
-
-  const aMutations: Vuex.MutationTree<IModuleAState> = {
-    INCREMENT (state: IModuleAState) {
-      state.value = state.value + 1;
+  const store = new Vuex.Store<RootState>({
+    state: {
+      value: 0
     }
-  };
-
-  const bMutations: Vuex.MutationTree<IModuleBState> = {
-    APPEND (state: IModuleBState, value: string) {
-      state.value = state.value + value;
-    }
-  };
+  });
 
-  const a = { state: aState, mutations: aMutations };
-  const b = { state: bState, mutations: bMutations };
+  store.registerModule("a", {
+    state: { value: 1 }
+  });
 
-  const store = new Vuex.Store<IModuleState>({
-    modules: { a, b }
+  store.registerModule(["a", "b"], {
+    state: { value: 2 }
   });
 
-  const valA: number = store.state.a.value;
-  const valB: string = store.state.b.value;
+  store.unregisterModule(["a", "b"]);
+  store.unregisterModule("a");
 }
 
-namespace TestPlugin {
-  const a = (store: Vuex.Store<any>) => {};
-
-  const b = (store: Vuex.Store<ISimpleState>) => {};
-
-  new Vuex.Store<ISimpleState>({
-    state: { count: 1 },
-    plugins: [a, b]
-  });
-}
+namespace HotUpdate {
+  interface RootState {
+    value: number;
+    a: {
+      b: {
+        value: number;
+      };
+    };
+  };
 
-namespace TestReplaceState {
-  const store = createStore();
+  type ActionStore = Vuex.ActionInjectee<{ value: number }, RootState>
 
-  store.replaceState({ count: 10 });
-}
+  const getters = {
+    rootValue: (state: RootState) => state.value
+  };
 
-namespace TestWatch {
-  const store = createStore();
+  const actions = {
+    foo (store: ActionStore, payload: number) {}
+  };
 
-  store.watch(state => state.count, value => {
-    const a: number = value;
-  }, {
-    deep: true,
-    immidiate: true
-  });
-}
+  const mutations = {
+    bar (state: { value: number }, payload: number) {}
+  };
 
-namespace TestHotUpdate {
-  const store = createStore();
+  const module = {
+    state: {
+      value: 0
+    },
+    getters: {
+      count: (state: { value: number }) => state.value
+    },
+    actions,
+    mutations
+  };
 
-  store.hotUpdate({
-    mutations: {
-      INCREMENT (state) {
-        state.count += 10;
+  const modules = {
+    a: {
+      modules: {
+        b: module
       }
     }
+  };
+
+  const store = new Vuex.Store<RootState>({
+    state: {
+      value: 0
+    } as any,
+    getters,
+    actions,
+    mutations,
+    modules
   });
 
   store.hotUpdate({
-    modules: {
-      a: {
-        state: 1,
-        mutations: {
-          INCREMENT (state) {
-            state.value++;
-          }
-        }
-      },
-      b: {
-        state: 'test',
-        mutations: {
-          APPEND (state, value) {
-            state.value += value;
-          }
-        }
-      }
-    }
+    getters,
+    actions,
+    mutations,
+    modules
   });
 }
 
-namespace TestSubscribe {
-  const store = createStore();
-
-  const handler = (mutation: Vuex.MutationObject<any>, state: ISimpleState) => {
-    state.count += 1;
-  };
-
-  const unsubscribe = store.subscribe(handler);
-  unsubscribe();
-}
+namespace Plugins {
+  function plugin (store: Vuex.Store<{ value: number }>) {
+    store.subscribe((mutation, state) => {
+      mutation.type;
+      state.value;
+    });
+  }
 
-namespace TestLogger {
-  const logger = createLogger<ISimpleState>({
-    collapsed: false,
-    transformer: state => state.count,
-    mutationTransformer: m => m
+  const logger = createLogger<{ value: number }>({
+    collapsed: true,
+    transformer: state => state.value,
+    mutationTransformer: (mutation: { type: string }) => mutation.type
   });
 
-  new Vuex.Store<ISimpleState>({
-    state: { count: 1 },
-    plugins: [logger]
+  const store = new Vuex.Store<{ value: number }>({
+    state: {
+      value: 0
+    },
+    plugins: [plugin, logger]
   });
 }

+ 13 - 1
types/test/tsconfig.json

@@ -2,11 +2,23 @@
   "compilerOptions": {
     "module": "commonjs",
     "target": "es5",
-    "noImplicitAny": true
+    "lib": [
+      "es5",
+      "dom",
+      "es2015.promise",
+      "es2015.core"
+    ],
+    "noImplicitAny": true,
+    "strictNullChecks": true,
+    "noEmit": true
   },
   "files": [
     "index.ts",
+    "helpers.ts",
     "../index.d.ts",
+    "../helpers.d.ts",
+    "../../logger.d.ts",
+    "../vue.d.ts",
     "../typings/index.d.ts"
   ]
 }

+ 10 - 1
types/tsconfig.json

@@ -2,10 +2,19 @@
   "compilerOptions": {
     "module": "commonjs",
     "target": "es5",
-    "noImplicitAny": true
+    "lib": [
+      "es5",
+      "dom",
+      "es2015.promise"
+    ],
+    "noImplicitAny": true,
+    "strictNullChecks": true
   },
   "files": [
     "index.d.ts",
+    "helpers.d.ts",
+    "../logger.d.ts",
+    "vue.d.ts",
     "typings/index.d.ts"
   ]
 }

+ 17 - 0
types/vue.d.ts

@@ -0,0 +1,17 @@
+/**
+ * Extends interfaces in Vue.js
+ */
+
+import { Store } from './index'
+
+declare global {
+  namespace vuejs {
+    interface ComponentOption {
+      store?: Store<any>;
+    }
+
+    interface Vue {
+      $store?: Store<any>;
+    }
+  }
+}