Skip to content

Commit

Permalink
fix(types): avoid broadening vue instance type when using map helpers (
Browse files Browse the repository at this point in the history
…#1639)

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

* refactor: add type restriction to helper types
  • Loading branch information
ktsn authored Nov 10, 2019
1 parent adab4aa commit 9a96720
Show file tree
Hide file tree
Showing 4 changed files with 165 additions and 110 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
69 changes: 43 additions & 26 deletions types/helpers.d.ts
Original file line number Diff line number Diff line change
@@ -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>
Expand All @@ -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;
196 changes: 117 additions & 79 deletions types/test/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,139 +11,177 @@ 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
})
}
}),

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
})
}
}),

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()
}
});
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down

0 comments on commit 9a96720

Please sign in to comment.