-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
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
feat: improve helper types for more type safety #1121
Changes from 9 commits
0768c80
25775c5
21ee399
eee1f3c
f94cf70
a5c4e26
58d28a5
0858c6d
1e27c5e
c2068f3
7abf34f
9564b80
1aa407f
cfb6042
00360b5
9b89ae7
09475d5
8e0c60b
b14662c
b24d744
8b6a6f9
9b183f0
715eaad
fc1f29b
c3626f7
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,100 @@ | ||
import Vue from 'vue'; | ||
import { Dispatch, Commit } from './index'; | ||
|
||
type Dictionary<T> = { [key: string]: T }; | ||
type Computed = () => any; | ||
type MutationMethod = (...args: any[]) => void; | ||
type ActionMethod = (...args: any[]) => Promise<any>; | ||
type CustomVue = Vue & Dictionary<any> | ||
|
||
interface Mapper<R> { | ||
(map: string[]): Dictionary<R>; | ||
(map: Dictionary<string>): Dictionary<R>; | ||
/** | ||
* Utility types to declare helper types | ||
*/ | ||
type Computed<R> = () => R; | ||
type Method<R> = (...args: any[]) => R; | ||
type MutationMethod<P> = (payload: P) => void; | ||
type ActionMethod<P> = (payload: P) => Promise<any>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm fine with untyped action return type. In fact, users can register multiple actions so the return type can't be checked any way. |
||
type CustomVue = Vue & Record<string, any>; | ||
|
||
interface BaseType { [key: string]: any } | ||
|
||
interface BaseStateMap<State, Getters> { | ||
[key: string]: (this: CustomVue, state: State, getters: Getters) => any; | ||
} | ||
|
||
interface BaseMethodMap<F> { | ||
[key: string]: (this: CustomVue, fn: F, ...args: any[]) => any; | ||
} | ||
|
||
/** | ||
* mapGetters | ||
*/ | ||
interface MapGetters<Getters> { | ||
<Key extends keyof Getters>(map: Key[]): { [K in Key]: Computed<Getters[K]> }; | ||
<Map extends Record<string, keyof Getters>>(map: Map): { [K in keyof Map]: Computed<Getters[Map[K]]> }; | ||
} | ||
|
||
interface RootMapGetters<Getters> extends MapGetters<Getters> { | ||
<Key extends keyof Getters>(namespace: string, map: Key[]): { [K in Key]: Computed<Getters[K]> }; | ||
<Map extends Record<string, keyof Getters>>(namespace: string, map: Map): { [K in keyof Map]: Computed<Getters[Map[K]]> }; | ||
} | ||
|
||
/** | ||
* mapState | ||
*/ | ||
interface MapState<State, Getters> { | ||
<Key extends keyof State>(map: Key[]): { [K in Key]: Computed<State[K]> }; | ||
<Map extends Record<string, keyof State>>(map: Map): { [K in keyof Map]: Computed<State[Map[K]]> }; | ||
<Map extends BaseStateMap<State, Getters>>(map: Map): { [K in keyof Map]: Computed<any> }; | ||
} | ||
|
||
interface MapperWithNamespace<R> { | ||
(namespace: string, map: string[]): Dictionary<R>; | ||
(namespace: string, map: Dictionary<string>): Dictionary<R>; | ||
interface RootMapState<State, Getters> extends MapState<State, Getters> { | ||
<Key extends keyof State>(namespace: string, map: Key[]): { [K in Key]: Computed<State[K]> }; | ||
<Map extends Record<string, keyof State>>(namespace: string, map: Map): { [K in keyof Map]: Computed<State[Map[K]]> }; | ||
<Map extends BaseStateMap<State, Getters>>(namespace: string, map: Map): { [K in keyof Map]: Computed<any> }; | ||
} | ||
|
||
interface FunctionMapper<F, R> { | ||
(map: Dictionary<(this: CustomVue, fn: F, ...args: any[]) => any>): Dictionary<R>; | ||
/** | ||
* mapMutations | ||
*/ | ||
interface MapMutations<Mutations> { | ||
<Key extends keyof Mutations>(map: Key[]): { [K in Key]: MutationMethod<Mutations[K]> }; | ||
<Map extends Record<string, keyof Mutations>>(map: Map): { [K in keyof Map]: MutationMethod<Mutations[Map[K]]> }; | ||
<Map extends BaseMethodMap<Commit<Mutations>>>(map: Map): { [K in keyof Map]: Method<any> }; | ||
} | ||
|
||
interface FunctionMapperWithNamespace<F, R> { | ||
( | ||
namespace: string, | ||
map: Dictionary<(this: CustomVue, fn: F, ...args: any[]) => any> | ||
): Dictionary<R>; | ||
interface RootMapMutations<Mutations> extends MapMutations<Mutations> { | ||
<Key extends keyof Mutations>(namespace: string, map: Key[]): { [K in Key]: MutationMethod<Mutations[K]> }; | ||
<Map extends Record<string, keyof Mutations>>(namespace: string, map: Map): { [K in keyof Map]: MutationMethod<Mutations[Map[K]]> }; | ||
<Map extends BaseMethodMap<Commit<Mutations>>>(namespace: string, map: Map): { [K in keyof Map]: Method<any> }; | ||
} | ||
|
||
interface MapperForState { | ||
<S>( | ||
map: Dictionary<(this: CustomVue, state: S, getters: any) => any> | ||
): Dictionary<Computed>; | ||
/** | ||
* mapActions | ||
*/ | ||
interface MapActions<Actions> { | ||
<Key extends keyof Actions>(map: Key[]): { [K in Key]: ActionMethod<Actions[K]> }; | ||
<Map extends Record<string, keyof Actions>>(map: Map): { [K in keyof Map]: ActionMethod<Actions[Map[K]]> }; | ||
<Map extends BaseMethodMap<Dispatch<Actions>>>(map: Map): { [K in keyof Map]: Method<any> }; | ||
} | ||
|
||
interface MapperForStateWithNamespace { | ||
<S>( | ||
namespace: string, | ||
map: Dictionary<(this: CustomVue, state: S, getters: any) => any> | ||
): Dictionary<Computed>; | ||
interface RootMapActions<Actions> extends MapActions<Actions> { | ||
<Key extends keyof Actions>(namespace: string, map: Key[]): { [K in Key]: ActionMethod<Actions[K]> }; | ||
<Map extends Record<string, keyof Actions>>(namespace: string, map: Map): { [K in keyof Map]: ActionMethod<Actions[Map[K]]> }; | ||
<Map extends BaseMethodMap<Dispatch<Actions>>>(namespace: string, map: Map): { [K in keyof Map]: Method<any> }; | ||
} | ||
|
||
interface NamespacedMappers { | ||
mapState: Mapper<Computed> & MapperForState; | ||
mapMutations: Mapper<MutationMethod> & FunctionMapper<Commit, MutationMethod>; | ||
mapGetters: Mapper<Computed>; | ||
mapActions: Mapper<ActionMethod> & FunctionMapper<Dispatch, ActionMethod>; | ||
/** | ||
* namespaced helpers | ||
*/ | ||
interface NamespacedMappers<State, Getters, Mutations, Actions> { | ||
mapState: MapState<State, Getters>; | ||
mapGetters: MapGetters<Getters>; | ||
mapMutations: MapMutations<Mutations>; | ||
mapActions: MapActions<Actions>; | ||
} | ||
|
||
export declare const mapState: Mapper<Computed> | ||
& MapperWithNamespace<Computed> | ||
& MapperForState | ||
& MapperForStateWithNamespace; | ||
export declare const mapState: RootMapState<BaseType, BaseType>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The proposed usage is Another usage is that we export export interface RootMapState<State> {
<Keys extends keyof State>(keys: Keys[]): {[K in Keys]: State[K]}
} then users can write something like But it is more versatile in usage -- I think object style can be supported, and more precise in returning type. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about reusing the pattern of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That sounds good idea. I think returning root helpers from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Awesome! Great balance between types and runtime behavior. |
||
|
||
export declare const mapMutations: Mapper<MutationMethod> | ||
& MapperWithNamespace<MutationMethod> | ||
& FunctionMapper<Commit, MutationMethod> | ||
& FunctionMapperWithNamespace<Commit, MutationMethod>; | ||
export declare const mapMutations: RootMapMutations<BaseType>; | ||
|
||
export declare const mapGetters: Mapper<Computed> | ||
& MapperWithNamespace<Computed>; | ||
export declare const mapGetters: RootMapGetters<BaseType>; | ||
|
||
export declare const mapActions: Mapper<ActionMethod> | ||
& MapperWithNamespace<ActionMethod> | ||
& FunctionMapper<Dispatch, ActionMethod> | ||
& FunctionMapperWithNamespace<Dispatch, ActionMethod>; | ||
export declare const mapActions: RootMapActions<BaseType>; | ||
|
||
export declare function createNamespacedHelpers(namespace: string): NamespacedMappers; | ||
export declare function createNamespacedHelpers(namespace?: string): NamespacedMappers<BaseType, BaseType, BaseType, BaseType>; | ||
export declare function createNamespacedHelpers<State, Getters, Mutations, Actions>(namespace?: string): NamespacedMappers<State, Getters, Mutations, Actions>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, what about
Mentioning TypeScript explicitly makes JS users know annotating type doesn't require much care for them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It sounds clearer than before. Thanks!