Bläddra i källkod

feat: root actions in namespaced modules (#941)

* Namespaced modules: root actions

* Refactor

* Package-lock auto updated

* Update dependencies
Guillaume Chau 7 år sedan
förälder
incheckning
73189eb355
7 ändrade filer med 215 tillägg och 76 borttagningar
  1. 109 63
      package-lock.json
  2. 2 2
      package.json
  3. 24 6
      src/module/module-collection.js
  4. 3 2
      src/store.js
  5. 57 2
      test/unit/modules.spec.js
  6. 7 1
      types/index.d.ts
  7. 13 0
      types/test/index.ts

+ 109 - 63
package-lock.json

@@ -1349,7 +1349,6 @@
       "requires": {
       "requires": {
         "anymatch": "1.3.2",
         "anymatch": "1.3.2",
         "async-each": "1.0.1",
         "async-each": "1.0.1",
-        "fsevents": "1.1.2",
         "glob-parent": "2.0.0",
         "glob-parent": "2.0.0",
         "inherits": "2.0.3",
         "inherits": "2.0.3",
         "is-binary-path": "1.0.1",
         "is-binary-path": "1.0.1",
@@ -1359,9 +1358,9 @@
       }
       }
     },
     },
     "chromedriver": {
     "chromedriver": {
-      "version": "2.31.0",
-      "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.31.0.tgz",
-      "integrity": "sha1-gsdY7kAVqogPRQCLujRkM8+dsUo=",
+      "version": "2.32.3",
+      "resolved": "https://registry.npmjs.org/chromedriver/-/chromedriver-2.32.3.tgz",
+      "integrity": "sha1-zoTwVb7ny/5W8xGCsnbzMlaxK/E=",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
         "extract-zip": "1.6.5",
         "extract-zip": "1.6.5",
@@ -1593,9 +1592,9 @@
       "dev": true
       "dev": true
     },
     },
     "content-type": {
     "content-type": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.2.tgz",
-      "integrity": "sha1-t9ETrueo3Se9IRM8TcJSnfFyHu0=",
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
+      "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
       "dev": true
       "dev": true
     },
     },
     "convert-source-map": {
     "convert-source-map": {
@@ -2434,9 +2433,9 @@
       "dev": true
       "dev": true
     },
     },
     "etag": {
     "etag": {
-      "version": "1.8.0",
-      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.0.tgz",
-      "integrity": "sha1-b2Ma7zNtbEY2K1F2QETOIWvjwFE=",
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+      "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
       "dev": true
       "dev": true
     },
     },
     "event-emitter": {
     "event-emitter": {
@@ -2511,41 +2510,50 @@
       }
       }
     },
     },
     "express": {
     "express": {
-      "version": "4.15.4",
-      "resolved": "https://registry.npmjs.org/express/-/express-4.15.4.tgz",
-      "integrity": "sha1-Ay4iU0ic+PzgJma+yj0R7XotrtE=",
+      "version": "4.15.5",
+      "resolved": "https://registry.npmjs.org/express/-/express-4.15.5.tgz",
+      "integrity": "sha1-ZwI1ypWYiQpa6BcLg9tyK4Qu2Sc=",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
         "accepts": "1.3.4",
         "accepts": "1.3.4",
         "array-flatten": "1.1.1",
         "array-flatten": "1.1.1",
         "content-disposition": "0.5.2",
         "content-disposition": "0.5.2",
-        "content-type": "1.0.2",
+        "content-type": "1.0.4",
         "cookie": "0.3.1",
         "cookie": "0.3.1",
         "cookie-signature": "1.0.6",
         "cookie-signature": "1.0.6",
-        "debug": "2.6.8",
+        "debug": "2.6.9",
         "depd": "1.1.1",
         "depd": "1.1.1",
         "encodeurl": "1.0.1",
         "encodeurl": "1.0.1",
         "escape-html": "1.0.3",
         "escape-html": "1.0.3",
-        "etag": "1.8.0",
-        "finalhandler": "1.0.4",
-        "fresh": "0.5.0",
+        "etag": "1.8.1",
+        "finalhandler": "1.0.6",
+        "fresh": "0.5.2",
         "merge-descriptors": "1.0.1",
         "merge-descriptors": "1.0.1",
         "methods": "1.1.2",
         "methods": "1.1.2",
         "on-finished": "2.3.0",
         "on-finished": "2.3.0",
-        "parseurl": "1.3.1",
+        "parseurl": "1.3.2",
         "path-to-regexp": "0.1.7",
         "path-to-regexp": "0.1.7",
         "proxy-addr": "1.1.5",
         "proxy-addr": "1.1.5",
         "qs": "6.5.0",
         "qs": "6.5.0",
         "range-parser": "1.2.0",
         "range-parser": "1.2.0",
-        "send": "0.15.4",
-        "serve-static": "1.12.4",
+        "send": "0.15.6",
+        "serve-static": "1.12.6",
         "setprototypeof": "1.0.3",
         "setprototypeof": "1.0.3",
         "statuses": "1.3.1",
         "statuses": "1.3.1",
         "type-is": "1.6.15",
         "type-is": "1.6.15",
         "utils-merge": "1.0.0",
         "utils-merge": "1.0.0",
-        "vary": "1.1.1"
+        "vary": "1.1.2"
       },
       },
       "dependencies": {
       "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
         "qs": {
         "qs": {
           "version": "6.5.0",
           "version": "6.5.0",
           "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz",
           "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.0.tgz",
@@ -2686,18 +2694,29 @@
       }
       }
     },
     },
     "finalhandler": {
     "finalhandler": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.4.tgz",
-      "integrity": "sha512-16l/r8RgzlXKmFOhZpHBztvye+lAhC5SU7hXavnerC9UfZqZxxXl3BzL8MhffPT3kF61lj9Oav2LKEzh0ei7tg==",
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.0.6.tgz",
+      "integrity": "sha1-AHrqM9Gk0+QgF/YkhIrVjSEvgU8=",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
-        "debug": "2.6.8",
+        "debug": "2.6.9",
         "encodeurl": "1.0.1",
         "encodeurl": "1.0.1",
         "escape-html": "1.0.3",
         "escape-html": "1.0.3",
         "on-finished": "2.3.0",
         "on-finished": "2.3.0",
-        "parseurl": "1.3.1",
+        "parseurl": "1.3.2",
         "statuses": "1.3.1",
         "statuses": "1.3.1",
         "unpipe": "1.0.0"
         "unpipe": "1.0.0"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        }
       }
       }
     },
     },
     "find-cache-dir": {
     "find-cache-dir": {
@@ -2771,15 +2790,15 @@
       }
       }
     },
     },
     "forwarded": {
     "forwarded": {
-      "version": "0.1.0",
-      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.0.tgz",
-      "integrity": "sha1-Ge+YdMSuHCl7zweP3mOgm2aoQ2M=",
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
+      "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
       "dev": true
       "dev": true
     },
     },
     "fresh": {
     "fresh": {
-      "version": "0.5.0",
-      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.0.tgz",
-      "integrity": "sha1-9HTKXmqSRtb9jglTz6m5yAWvp44=",
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+      "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
       "dev": true
       "dev": true
     },
     },
     "fs-extra": {
     "fs-extra": {
@@ -5678,9 +5697,9 @@
       }
       }
     },
     },
     "parseurl": {
     "parseurl": {
-      "version": "1.3.1",
-      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.1.tgz",
-      "integrity": "sha1-yKuMkiO6NIiKpkopeyiFO+wY2lY=",
+      "version": "1.3.2",
+      "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.2.tgz",
+      "integrity": "sha1-/CidTtiZMRlGDBViUyYs3I3mW/M=",
       "dev": true
       "dev": true
     },
     },
     "path-browserify": {
     "path-browserify": {
@@ -6468,7 +6487,7 @@
       "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=",
       "integrity": "sha1-ccDuOxAt4/IC87ZPYI0XP8uhqRg=",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
-        "forwarded": "0.1.0",
+        "forwarded": "0.1.2",
         "ipaddr.js": "1.4.0"
         "ipaddr.js": "1.4.0"
       }
       }
     },
     },
@@ -7010,6 +7029,12 @@
         "rollup-pluginutils": "2.0.1"
         "rollup-pluginutils": "2.0.1"
       },
       },
       "dependencies": {
       "dependencies": {
+        "estree-walker": {
+          "version": "0.3.1",
+          "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.3.1.tgz",
+          "integrity": "sha1-5rGlHPcpJSTnI3wxLl/mZgwc4ao=",
+          "dev": true
+        },
         "magic-string": {
         "magic-string": {
           "version": "0.22.4",
           "version": "0.22.4",
           "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz",
           "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.22.4.tgz",
@@ -7018,6 +7043,16 @@
           "requires": {
           "requires": {
             "vlq": "0.2.2"
             "vlq": "0.2.2"
           }
           }
+        },
+        "rollup-pluginutils": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.0.1.tgz",
+          "integrity": "sha1-fslbNXP2VDpGpkYb2afFRFJdD8A=",
+          "dev": true,
+          "requires": {
+            "estree-walker": "0.3.1",
+            "micromatch": "2.3.11"
+          }
         }
         }
       }
       }
     },
     },
@@ -7089,36 +7124,47 @@
       "dev": true
       "dev": true
     },
     },
     "send": {
     "send": {
-      "version": "0.15.4",
-      "resolved": "https://registry.npmjs.org/send/-/send-0.15.4.tgz",
-      "integrity": "sha1-mF+qPihLAnPHkzZKNcZze9k5Bbk=",
+      "version": "0.15.6",
+      "resolved": "https://registry.npmjs.org/send/-/send-0.15.6.tgz",
+      "integrity": "sha1-IPI6nJJbdiq4JwX+L52yUqzkfjQ=",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
-        "debug": "2.6.8",
+        "debug": "2.6.9",
         "depd": "1.1.1",
         "depd": "1.1.1",
         "destroy": "1.0.4",
         "destroy": "1.0.4",
         "encodeurl": "1.0.1",
         "encodeurl": "1.0.1",
         "escape-html": "1.0.3",
         "escape-html": "1.0.3",
-        "etag": "1.8.0",
-        "fresh": "0.5.0",
+        "etag": "1.8.1",
+        "fresh": "0.5.2",
         "http-errors": "1.6.2",
         "http-errors": "1.6.2",
         "mime": "1.3.4",
         "mime": "1.3.4",
         "ms": "2.0.0",
         "ms": "2.0.0",
         "on-finished": "2.3.0",
         "on-finished": "2.3.0",
         "range-parser": "1.2.0",
         "range-parser": "1.2.0",
         "statuses": "1.3.1"
         "statuses": "1.3.1"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+          "dev": true,
+          "requires": {
+            "ms": "2.0.0"
+          }
+        }
       }
       }
     },
     },
     "serve-static": {
     "serve-static": {
-      "version": "1.12.4",
-      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.4.tgz",
-      "integrity": "sha1-m2qpjutyU8Tu3Ewfb9vKYJkBqWE=",
+      "version": "1.12.6",
+      "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.12.6.tgz",
+      "integrity": "sha1-uXN3P2NEmTTaVOW+ul4x2fQhFXc=",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
         "encodeurl": "1.0.1",
         "encodeurl": "1.0.1",
         "escape-html": "1.0.3",
         "escape-html": "1.0.3",
-        "parseurl": "1.3.1",
-        "send": "0.15.4"
+        "parseurl": "1.3.2",
+        "send": "0.15.6"
       }
       }
     },
     },
     "set-blocking": {
     "set-blocking": {
@@ -7616,9 +7662,9 @@
       "dev": true
       "dev": true
     },
     },
     "typescript": {
     "typescript": {
-      "version": "2.4.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.4.2.tgz",
-      "integrity": "sha1-+DlfhdRZJ2BnyYiqQYN6j4KHCEQ=",
+      "version": "2.5.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.5.3.tgz",
+      "integrity": "sha512-ptLSQs2S4QuS6/OD1eAKG+S5G8QQtrU5RT32JULdZQtM1L3WTi34Wsu48Yndzi8xsObRAB9RPt/KhA9wlpEF6w==",
       "dev": true
       "dev": true
     },
     },
     "uglify-js": {
     "uglify-js": {
@@ -7782,9 +7828,9 @@
       }
       }
     },
     },
     "vary": {
     "vary": {
-      "version": "1.1.1",
-      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.1.tgz",
-      "integrity": "sha1-Z1Neu2lMHVIldFeYRmUyP1h+jTc=",
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+      "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
       "dev": true
       "dev": true
     },
     },
     "vendors": {
     "vendors": {
@@ -7828,9 +7874,9 @@
       }
       }
     },
     },
     "vue": {
     "vue": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/vue/-/vue-2.4.3.tgz",
-      "integrity": "sha512-k6zkIBR0KsE0DLUDGdRLooX/4iRUbc3T2FyrJs4YhVySbjGwS3k5c2HRCHyXo6lg1aeAF9rg3uiJDRz0J7nbDA==",
+      "version": "2.4.4",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-2.4.4.tgz",
+      "integrity": "sha512-PCiRmc8ZT1DD5+BN8QUAmnkBefcCLfZVSuhc1u7iu5JoPrSHyyk/+4nehm7k2xVMi8+RFLk5WIHAN14UKF0txw==",
       "dev": true
       "dev": true
     },
     },
     "vue-hot-reload-api": {
     "vue-hot-reload-api": {
@@ -7919,9 +7965,9 @@
       }
       }
     },
     },
     "vue-template-compiler": {
     "vue-template-compiler": {
-      "version": "2.4.3",
-      "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.4.3.tgz",
-      "integrity": "sha512-rtHVKIFjd3Ynb+9FSoA64m2h2SPTEVKk6PywkqbugpM0nxT3ykLFyhbLTdSX1qV5wI9h5DAR4ib4RubEFfyiBQ==",
+      "version": "2.4.4",
+      "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.4.4.tgz",
+      "integrity": "sha512-XdHsNi8Z5WqwuFl/Z5eLKgE2DOEEOdMk1aA459uSgvwyy+pjKLBlQWsUpAtoR6o6Wmpujw6NtinAUGuqSTituQ==",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
         "de-indent": "1.0.2",
         "de-indent": "1.0.2",
@@ -8018,9 +8064,9 @@
       }
       }
     },
     },
     "webpack-hot-middleware": {
     "webpack-hot-middleware": {
-      "version": "2.18.2",
-      "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.18.2.tgz",
-      "integrity": "sha512-dB7uOnUWsojZIAC6Nwi5v3tuaQNd2i7p4vF5LsJRyoTOgr2fRYQdMKQxRZIZZaz0cTPBX8rvcWU1A6/n7JTITg==",
+      "version": "2.19.1",
+      "resolved": "https://registry.npmjs.org/webpack-hot-middleware/-/webpack-hot-middleware-2.19.1.tgz",
+      "integrity": "sha512-2x60xmz7XBCNN/Drol+7i85E/5RrNrf+ivOPCgrxhP1F3q3WxpVjjvj8n8fOS1bS9oTRKEDfBYVAtkxqsG7LwQ==",
       "dev": true,
       "dev": true,
       "requires": {
       "requires": {
         "ansi-html": "0.0.7",
         "ansi-html": "0.0.7",

+ 2 - 2
package.json

@@ -44,7 +44,7 @@
     "babel-plugin-transform-object-rest-spread": "^6.23.0",
     "babel-plugin-transform-object-rest-spread": "^6.23.0",
     "babel-polyfill": "^6.22.0",
     "babel-polyfill": "^6.22.0",
     "babel-preset-env": "^1.5.1",
     "babel-preset-env": "^1.5.1",
-    "chromedriver": "^2.27.2",
+    "chromedriver": "^2.32.3",
     "cross-spawn": "^5.0.1",
     "cross-spawn": "^5.0.1",
     "css-loader": "^0.28.7",
     "css-loader": "^0.28.7",
     "eslint": "^3.19.0",
     "eslint": "^3.19.0",
@@ -72,6 +72,6 @@
     "vue-template-compiler": "^2.4.3",
     "vue-template-compiler": "^2.4.3",
     "webpack": "^3.6.0",
     "webpack": "^3.6.0",
     "webpack-dev-middleware": "^1.10.0",
     "webpack-dev-middleware": "^1.10.0",
-    "webpack-hot-middleware": "^2.16.1"
+    "webpack-hot-middleware": "^2.19.1"
   }
   }
 }
 }

+ 24 - 6
src/module/module-collection.js

@@ -84,25 +84,43 @@ function update (path, targetModule, newModule) {
   }
   }
 }
 }
 
 
+const functionAssert = {
+  assert: value => typeof value === 'function',
+  expected: 'function'
+}
+
+const objectAssert = {
+  assert: value => typeof value === 'function' ||
+    (typeof value === 'object' && typeof value.handler === 'function'),
+  expected: 'function or object with "handler" function'
+}
+
+const assertTypes = {
+  getters: functionAssert,
+  mutations: functionAssert,
+  actions: objectAssert
+}
+
 function assertRawModule (path, rawModule) {
 function assertRawModule (path, rawModule) {
-  ['getters', 'actions', 'mutations'].forEach(key => {
+  Object.keys(assertTypes).forEach(key => {
     if (!rawModule[key]) return
     if (!rawModule[key]) return
 
 
+    const assertOptions = assertTypes[key]
+
     forEachValue(rawModule[key], (value, type) => {
     forEachValue(rawModule[key], (value, type) => {
       assert(
       assert(
-        typeof value === 'function',
-        makeAssertionMessage(path, key, type, value)
+        assertOptions.assert(value),
+        makeAssertionMessage(path, key, type, value, assertOptions.expected)
       )
       )
     })
     })
   })
   })
 }
 }
 
 
-function makeAssertionMessage (path, key, type, value) {
-  let buf = `${key} should be function but "${key}.${type}"`
+function makeAssertionMessage (path, key, type, value, expected) {
+  let buf = `${key} should be ${expected} but "${key}.${type}"`
   if (path.length > 0) {
   if (path.length > 0) {
     buf += ` in module "${path.join('.')}"`
     buf += ` in module "${path.join('.')}"`
   }
   }
   buf += ` is ${JSON.stringify(value)}.`
   buf += ` is ${JSON.stringify(value)}.`
-
   return buf
   return buf
 }
 }

+ 3 - 2
src/store.js

@@ -287,8 +287,9 @@ function installModule (store, rootState, path, module, hot) {
   })
   })
 
 
   module.forEachAction((action, key) => {
   module.forEachAction((action, key) => {
-    const namespacedType = namespace + key
-    registerAction(store, namespacedType, action, local)
+    const type = action.root ? key : namespace + key
+    const handler = action.handler || action
+    registerAction(store, type, handler, local)
   })
   })
 
 
   module.forEachGetter((getter, key) => {
   module.forEachGetter((getter, key) => {

+ 57 - 2
test/unit/modules.spec.js

@@ -573,6 +573,61 @@ describe('Modules', () => {
       })
       })
     })
     })
 
 
+    it('root actions dispatched in namespaced modules', done => {
+      const store = new Vuex.Store({
+        modules: {
+          a: {
+            namespaced: true,
+            actions: {
+              [TEST]: {
+                root: true,
+                handler () {
+                  return 1
+                }
+              }
+            }
+          },
+          b: {
+            namespaced: true,
+            actions: {
+              [TEST]: {
+                root: true,
+                handler () {
+                  return new Promise(r => r(2))
+                }
+              }
+            }
+          },
+          c: {
+            namespaced: true,
+            actions: {
+              [TEST]: {
+                handler () {
+                  // Should not be called
+                  return 3
+                }
+              }
+            }
+          },
+          d: {
+            namespaced: true,
+            actions: {
+              [TEST] () {
+                // Should not be called
+                return 4
+              }
+            }
+          }
+        }
+      })
+      store.dispatch(TEST).then(res => {
+        expect(res.length).toBe(2)
+        expect(res[0]).toBe(1)
+        expect(res[1]).toBe(2)
+        done()
+      })
+    })
+
     it('plugins', function () {
     it('plugins', function () {
       let initState
       let initState
       const mutations = []
       const mutations = []
@@ -641,7 +696,7 @@ describe('Modules', () => {
         }
         }
       })
       })
     }).toThrowError(
     }).toThrowError(
-      /actions should be function but "actions\.test" is "test"/
+      /actions should be function or object with "handler" function but "actions\.test" is "test"/
     )
     )
 
 
     expect(() => {
     expect(() => {
@@ -659,7 +714,7 @@ describe('Modules', () => {
         }
         }
       })
       })
     }).toThrowError(
     }).toThrowError(
-      /actions should be function but "actions\.test" in module "foo\.bar" is "error"/
+      /actions should be function or object with "handler" function but "actions\.test" in module "foo\.bar" is "error"/
     )
     )
   })
   })
 
 

+ 7 - 1
types/index.d.ts

@@ -82,8 +82,14 @@ export interface StoreOptions<S> {
   strict?: boolean;
   strict?: boolean;
 }
 }
 
 
+type ActionHandler<S, R> = (injectee: ActionContext<S, R>, payload: any) => any;
+interface ActionObject<S, R> {
+  root?: boolean;
+  handler: ActionHandler<S, R>;
+}
+
 export type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;
 export type Getter<S, R> = (state: S, getters: any, rootState: R, rootGetters: any) => any;
-export type Action<S, R> = (injectee: ActionContext<S, R>, payload: any) => any;
+export type Action<S, R> = ActionHandler<S, R> | ActionObject<S, R>;
 export type Mutation<S> = (state: S, payload: any) => any;
 export type Mutation<S> = (state: S, payload: any) => any;
 export type Plugin<S> = (store: Store<S>) => any;
 export type Plugin<S> = (store: Store<S>) => any;
 
 

+ 13 - 0
types/test/index.ts

@@ -138,6 +138,19 @@ namespace NamespacedModule {
       a: {
       a: {
         namespaced: true,
         namespaced: true,
         state: { value: 1 },
         state: { value: 1 },
+        actions: {
+          test: {
+            root: true,
+            handler ({ dispatch }) {
+              dispatch('foo')
+            }
+          },
+          test2: {
+            handler ({ dispatch }) {
+              dispatch('foo')
+            }
+          }
+        },
         modules: {
         modules: {
           b: {
           b: {
             state: { value: 2 }
             state: { value: 2 }