Sfoglia il codice sorgente

fix(types): avoid broadening vue instance type when using map helpers (#1639)

* fix(types): avoid broading vue instance type when using map helpers

* refactor: add type restriction to helper types
katashin 5 anni fa
parent
commit
9a9672050b
4 ha cambiato i file con 165 aggiunte e 110 eliminazioni
  1. 1 1
      package.json
  2. 43 26
      types/helpers.d.ts
  3. 117 79
      types/test/helpers.ts
  4. 4 4
      yarn.lock

+ 1 - 1
package.json

@@ -63,7 +63,7 @@
     "selenium-server": "^2.53.1",
     "terser": "^3.17.0",
     "todomvc-app-css": "^2.1.0",
-    "typescript": "^3.2.2",
+    "typescript": "^3.7.2",
     "vue": "^2.5.22",
     "vue-loader": "^15.2.1",
     "vue-template-compiler": "^2.5.22",

+ 43 - 26
types/helpers.d.ts

@@ -1,51 +1,68 @@
 import Vue from 'vue';
 import { Dispatch, Commit } from './index';
 
-type Dictionary<T> = { [key: string]: T };
 type Computed = () => any;
+type InlineComputed<T extends Function> = T extends (...args: any[]) => infer R ? () => R : never
 type MutationMethod = (...args: any[]) => void;
 type ActionMethod = (...args: any[]) => Promise<any>;
-type CustomVue = Vue & Dictionary<any>;
+type InlineMethod<T extends (fn: any, ...args: any[]) => any> = T extends (fn: any, ...args: infer Args) => infer R ? (...args: Args) => R : never
+type CustomVue = Vue & Record<string, any>;
 
 interface Mapper<R> {
-  (map: string[]): Dictionary<R>;
-  (map: Dictionary<string>): Dictionary<R>;
+  <Key extends string>(map: Key[]): { [K in Key]: R };
+  <Map extends Record<string, string>>(map: Map): { [K in keyof Map]: R };
 }
 
 interface MapperWithNamespace<R> {
-  (namespace: string, map: string[]): Dictionary<R>;
-  (namespace: string, map: Dictionary<string>): Dictionary<R>;
+  <Key extends string>(namespace: string, map: Key[]): { [K in Key]: R };
+  <Map extends Record<string, string>>(namespace: string, map: Map): { [K in keyof Map]: R };
 }
 
-interface FunctionMapper<F, R> {
-  (map: Dictionary<(this: CustomVue, fn: F, ...args: any[]) => any>): Dictionary<R>;
+interface MapperForState {
+  <S, Map extends Record<string, (this: CustomVue, state: S, getters: any) => any> = {}>(
+    map: Map
+  ): { [K in keyof Map]: InlineComputed<Map[K]> };
 }
 
-interface FunctionMapperWithNamespace<F, R> {
-  (
+interface MapperForStateWithNamespace {
+  <S, Map extends Record<string, (this: CustomVue, state: S, getters: any) => any> = {}>(
     namespace: string,
-    map: Dictionary<(this: CustomVue, fn: F, ...args: any[]) => any>
-  ): Dictionary<R>;
+    map: Map
+  ): { [K in keyof Map]: InlineComputed<Map[K]> };
 }
 
-interface MapperForState {
-  <S>(
-    map: Dictionary<(this: CustomVue, state: S, getters: any) => any>
-  ): Dictionary<Computed>;
+interface MapperForAction {
+  <Map extends Record<string, (this: CustomVue, dispatch: Dispatch, ...args: any[]) => any>>(
+    map: Map
+  ): { [K in keyof Map]: InlineMethod<Map[K]> };
 }
 
-interface MapperForStateWithNamespace {
-  <S>(
+interface MapperForActionWithNamespace {
+  <Map extends Record<string, (this: CustomVue, dispatch: Dispatch, ...args: any[]) => any>>(
+    namespace: string,
+    map: Map
+  ): { [K in keyof Map]: InlineMethod<Map[K]> };
+}
+
+interface MapperForMutation {
+  <Map extends Record<string, (this: CustomVue, commit: Commit, ...args: any[]) => any>>(
+    map: Map
+  ): { [K in keyof Map]: InlineMethod<Map[K]> };
+}
+
+interface MapperForMutationWithNamespace {
+  <Map extends Record<string, (this: CustomVue, commit: Commit, ...args: any[]) => any>>(
     namespace: string,
-    map: Dictionary<(this: CustomVue, state: S, getters: any) => any>
-  ): Dictionary<Computed>;
+    map: Map
+  ): { [K in keyof Map]: InlineMethod<Map[K]> };
 }
 
+
 interface NamespacedMappers {
   mapState: Mapper<Computed> & MapperForState;
-  mapMutations: Mapper<MutationMethod> & FunctionMapper<Commit, MutationMethod>;
+  mapMutations: Mapper<MutationMethod> & MapperForMutation;
   mapGetters: Mapper<Computed>;
-  mapActions: Mapper<ActionMethod> & FunctionMapper<Dispatch, ActionMethod>;
+  mapActions: Mapper<ActionMethod> & MapperForAction;
 }
 
 export declare const mapState: Mapper<Computed>
@@ -55,15 +72,15 @@ export declare const mapState: Mapper<Computed>
 
 export declare const mapMutations: Mapper<MutationMethod>
   & MapperWithNamespace<MutationMethod>
-  & FunctionMapper<Commit, MutationMethod>
-  & FunctionMapperWithNamespace<Commit, MutationMethod>;
+  & MapperForMutation
+  & MapperForMutationWithNamespace;
 
 export declare const mapGetters: Mapper<Computed>
   & MapperWithNamespace<Computed>;
 
 export declare const mapActions: Mapper<ActionMethod>
   & MapperWithNamespace<ActionMethod>
-  & FunctionMapper<Dispatch, ActionMethod>
-  & FunctionMapperWithNamespace<Dispatch, ActionMethod>;
+  & MapperForAction
+  & MapperForActionWithNamespace;
 
 export declare function createNamespacedHelpers(namespace: string): NamespacedMappers;

+ 117 - 79
types/test/helpers.ts

@@ -11,79 +11,78 @@ import {
 const helpers = createNamespacedHelpers('foo');
 
 new Vue({
-  computed: Object.assign({},
-    mapState(["a"]),
-    mapState('foo', ["a"]),
-    mapState({
-      b: "b"
+  computed: {
+    ...mapState(["a"]),
+    ...mapState('foo', ["b"]),
+    ...mapState({
+      c: "c"
     }),
-    mapState('foo', {
-      b: "b"
+    ...mapState('foo', {
+      d: "d"
     }),
-    mapState({
-      c: (state: any, getters: any) => state.c + getters.c
+    ...mapState({
+      e: (state: any, getters: any) => state.a + getters.g
     }),
-    mapState('foo', {
-      c: (state: any, getters: any) => state.c + getters.c
+    ...mapState('foo', {
+      f: (state: any, getters: any) => state.a + getters.g
     }),
 
-    mapGetters(["d"]),
-    mapGetters('foo', ["d"]),
-    mapGetters({
-      e: "e"
+    ...mapGetters(["g"]),
+    ...mapGetters('foo', ["h"]),
+    ...mapGetters({
+      i: "i"
     }),
-    mapGetters('foo', {
-      e: "e"
+    ...mapGetters('foo', {
+      j: "j"
     }),
 
-    helpers.mapState(["k"]),
-    helpers.mapState({
-      k: "k"
-    }),
-    helpers.mapState({
-      k: (state: any, getters: any) => state.k + getters.k,
-      useThis(state: any, getters: any) {
-        return state.k + getters.k + this.whatever
+    ...helpers.mapState(["k"]),
+    ...helpers.mapState({
+      l: "l"
+    }),
+    ...helpers.mapState({
+      m: (state: any, getters: any) => state.a + getters.g,
+      useThis(state: any, getters: any): any {
+        return state.a + getters.g + this.whatever
       }
     }),
 
-    helpers.mapGetters(["l"]),
-    helpers.mapGetters({
-      l: "l"
+    ...helpers.mapGetters(["n"]),
+    ...helpers.mapGetters({
+      o: "o"
     }),
 
-    {
-      otherComputed () {
-        return "f";
-      }
+
+    otherComputed () {
+      return "";
     }
-  ),
+  },
 
-  methods: Object.assign({},
-    mapActions(["g"]),
-    mapActions({
-      h: "h"
-    }),
-    mapActions({
-      g (dispatch, a: string, b: number, c: boolean): void {
-        dispatch('g', { a, b, c })
+  methods: {
+    ...mapActions(["p"]),
+    ...mapActions({
+      q: "q"
+    }),
+    ...mapActions({
+      r (dispatch, a: string, b: number, c: boolean) {
+        dispatch('p', { a, b, c })
         dispatch({
-          type: 'g',
+          type: 'p',
           a,
           b,
           c
         })
       }
     }),
-    mapActions('foo', ["g"]),
-    mapActions('foo', {
-      h: "h"
+    ...mapActions('foo', ["s"]),
+    ...mapActions('foo', {
+      t: "t"
     }),
-    mapActions('foo', {
-      g (dispatch, a: string, b: number, c: boolean): void {
-        dispatch('g', { a, b, c })
+    ...mapActions('foo', {
+      u (dispatch, a: string, b: number, c: boolean) {
+        dispatch('p', { a, b, c })
         dispatch({
-          type: 'g',
+          type: 'p',
           a,
           b,
           c
@@ -91,30 +90,30 @@ new Vue({
       }
     }),
 
-    mapMutations(["i"]),
-    mapMutations({
-      j: "j"
+    ...mapMutations(["v"]),
+    ...mapMutations({
+      w: "w"
     }),
-    mapMutations({
-      i (commit, a: string, b: number, c: boolean): void {
-        commit('i', { a, b, c })
+    ...mapMutations({
+      x (commit, a: string, b: number, c: boolean) {
+        commit('v', { a, b, c })
         commit({
-          type: 'i',
+          type: 'v',
           a,
           b,
           c
         })
       }
     }),
-    mapMutations('foo', ["i"]),
-    mapMutations('foo', {
-      j: "j"
+    ...mapMutations('foo', ["y"]),
+    ...mapMutations('foo', {
+      z: "z"
     }),
-    mapMutations('foo', {
-      i (commit, a: string, b: number, c: boolean): void {
-        commit('i', { a, b, c })
+    ...mapMutations('foo', {
+      aa (commit, a: string, b: number, c: boolean) {
+        commit('v', { a, b, c })
         commit({
-          type: 'i',
+          type: 'v',
           a,
           b,
           c
@@ -122,28 +121,67 @@ new Vue({
       }
     }),
 
-    helpers.mapActions(["m"]),
-    helpers.mapActions({
-      m: "m"
+    ...helpers.mapActions(["ab"]),
+    ...helpers.mapActions({
+      ac: "ac"
     }),
-    helpers.mapActions({
-      m (dispatch, value: string) {
-        dispatch('m', value)
+    ...helpers.mapActions({
+      ad (dispatch, value: string) {
+        dispatch('p', value)
       }
     }),
 
-    helpers.mapMutations(["n"]),
-    helpers.mapMutations({
-      n: "n"
+    ...helpers.mapMutations(["ae"]),
+    ...helpers.mapMutations({
+      af: "af"
     }),
-    helpers.mapMutations({
-      n (commit, value: string) {
-        commit('m', value)
+    ...helpers.mapMutations({
+      ag (commit, value: string) {
+        commit('v', value)
       }
     }),
 
-    {
-      otherMethod () {}
-    }
-  )
+    otherMethod () {}
+  },
+
+  created() {
+    // Computed
+    this.a
+    this.b
+    this.c
+    this.d
+    this.e
+    this.f
+    this.g
+    this.h
+    this.i
+    this.j
+    this.k
+    this.l
+    this.m
+    this.n
+    this.o
+    this.otherComputed
+
+    // Methods
+    this.p()
+    this.q()
+    this.r('', 0, true)
+    this.s()
+    this.t()
+    this.u('', 0, true)
+    this.v()
+    this.w()
+    this.x('', 0, true)
+    this.y()
+    this.z()
+    this.aa('', 0, true)
+    this.ab()
+    this.ac()
+    this.ad('')
+    this.ae()
+    this.af()
+    this.ag('')
+    this.otherMethod()
+  }
 });

+ 4 - 4
yarn.lock

@@ -9071,10 +9071,10 @@ typedarray@^0.0.6:
   resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
   integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
 
-typescript@^3.2.2:
-  version "3.2.2"
-  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.2.2.tgz#fe8101c46aa123f8353523ebdcf5730c2ae493e5"
-  integrity sha512-VCj5UiSyHBjwfYacmDuc/NOk4QQixbE+Wn7MFJuS0nRuPQbof132Pw4u53dm264O8LPc2MVsc7RJNml5szurkg==
+typescript@^3.7.2:
+  version "3.7.2"
+  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb"
+  integrity sha512-ml7V7JfiN2Xwvcer+XAf2csGO1bPBdRbFCkYBczNZggrBZ9c7G3riSUeJmqEU5uOtXNPMhE3n+R4FA/3YOAWOQ==
 
 uc.micro@^1.0.1, uc.micro@^1.0.5:
   version "1.0.5"