Skip to content
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(core/types): add chaining derivation support to mapState & mapGetters #674

Merged
merged 11 commits into from
Jan 13, 2021
8 changes: 6 additions & 2 deletions packages/core/@types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ export interface MpxComponentIns {

$watch (expr: string | (() => any), handler: WatchHandler | WatchOptWithHandler, options?: WatchOpt): () => void

$forceUpdate (params: object, callback: () => void): void
$forceUpdate (params?: object, callback?: () => void): void

$nextTick (fn: () => void): void

Expand Down Expand Up @@ -243,8 +243,9 @@ export function createStoreWithThis<S = {}, G = {}, M extends MutationsAndAction
// auxiliary functions
export function createStateWithThis<S = {}> (state: S): S

export function createGettersWithThis<S = {}, D extends Deps = {}, G = {}> (getters: G & ThisType<{ state: S & UnboxDepsField<D, 'state'>, getters: GetComputedType<G> & UnboxDepsField<D, 'getters'>, rootState: any }>, options?: {
export function createGettersWithThis<S = {}, D extends Deps = {}, G = {}, OG = {}> (getters: G & ThisType<{ state: S & UnboxDepsField<D, 'state'>, getters: GetComputedType<G & OG> & UnboxDepsField<D, 'getters'>, rootState: any }>, options?: {
state?: S,
getters?: OG,
deps?: D
}): G

Expand All @@ -259,6 +260,9 @@ export function createActionsWithThis<S = {}, G = {}, M extends MutationsAndActi
getters: GetComputedType<G> & UnboxDepsField<D, 'getters'>,
dispatch: GetDispatchAndCommitWithThis<A, D, 'actions'>,
commit: GetDispatchAndCommitWithThis<M, D, 'mutations'>
} & {
dispatch(type: string, ...payload: any[]): any
commit(type: string, ...payload: any[]): any
}>, options?: {
state?: S,
getters?: G,
Expand Down
161 changes: 94 additions & 67 deletions packages/core/@types/mpx-store.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
declare namespace MpxStore {
type UnboxDepField<D, F> = F extends keyof D ? D[F] : {}

interface Deps {
[key: string]: Store | StoreWithThis
}

type UnboxDepsField<D extends Deps, F> = string extends keyof D ? {} : {
[K in keyof D]: UnboxDepField<D[K], F>
}
Expand All @@ -13,7 +17,7 @@ declare namespace MpxStore {
[key: string]: (this: void, state: S, ...payload: any[]) => any
}

type Getters<S> = {
interface Getters<S> {
[key: string]: (this: void, state: S, getters: any, globalState: any) => any
}

Expand Down Expand Up @@ -43,11 +47,40 @@ declare namespace MpxStore {

type GetCommit<M, D> = keyof D extends never ? (<T extends keyof M>(type: T, ...payload: M[T] extends (state: any, ...payload: infer P) => any ? P : never) => M[T] extends (state: any, ...payload: any[]) => infer R ? R : never) : ((type: string, ...payload: any[]) => any)

interface Deps {
[key: string]: Store | StoreWithThis
}
interface Store<S = {}, G = {}, M = {}, A = {}, D extends Deps = {}> {

state: S & UnboxDepsField<D, 'state'>
getters: GetGetters<G> & UnboxDepsField<D, 'getters'>
mutations: GetMutations<M> & UnboxDepsField<D, 'mutations'>
actions: GetActions<A> & UnboxDepsField<D, 'actions'>

dispatch: GetDispatch<A, D>

commit: GetCommit<M, D>

mapState<K extends keyof S>(maps: K[]): {
[I in K]: () => S[I]
}
mapState(depPath: string, maps: string[]): object

mapGetters<K extends keyof G>(maps: K[]): {
[I in K]: () => GetGetters<G>[I]
}
mapGetters(depPath: string, maps: string[]): {
[key: string]: () => any
}

mapMutations<K extends keyof M>(maps: K[]): Pick<GetMutations<M>, K>
mapMutations(depPath: string, maps: string[]): {
[key: string]: (...payloads: any[]) => any
}

mapActions<K extends keyof A>(maps: K[]): Pick<GetActions<A>, K>
mapActions(depPath: string, maps: string[]): {
[key: string]: (...payloads: any[]) => any
}

}
type GetComputedSetKeys<T> = {
[K in keyof T]: T[K] extends {
get(): any,
Expand Down Expand Up @@ -77,11 +110,14 @@ declare namespace MpxStore {
interface mapStateFunctionType<S, G> {
[key: string]: (state: S, getter: G) => any
}

interface DeeperMutationsAndActions {
[key: string]: ((...payload: any[]) => any) | MutationsAndActionsWithThis
}

interface DeeperStateAndGetters {
[key: string]: any | DeeperStateAndGetters
}

// Store Type Bindings
type StringKeyof<T> = Exclude<keyof T, symbol>

Expand All @@ -93,17 +129,44 @@ declare namespace MpxStore {
}[CombineStringKey<P, K>]
}[StringKeyof<A>]> // {actA: () => void, storeB.actB: () => void}

type GetAllActionsKey<A, D extends Deps, AK extends 'actions' | 'mutations'> = {
type GetStateAndGettersKey<S, P extends string | number = ''> = UnionToIntersection<{
[K in StringKeyof<S>]: {
[RK in CombineStringKey<P, K>]: S[K] extends DeeperStateAndGetters ? GetStateAndGettersKey<S[K], RK> : Record<RK, S[K]>
}[CombineStringKey<P, K>]
}[StringKeyof<S>]> // {stateA: any, storeB.stateB: any}

type GetAllDepsType<A, D extends Deps, AK extends 'state' | 'getters' | 'actions' | 'mutations'> = {
[K in StringKeyof<A>]: A[K]
anotherso1a marked this conversation as resolved.
Show resolved Hide resolved
} & UnionToIntersection<{
[K in StringKeyof<D>]: {
[K in StringKeyof<D>]: AK extends 'actions' | 'mutations' ? {
[P in keyof GetActionsKey<D[K][AK], K>]: GetActionsKey<D[K][AK], K>[P]
} : { // state, getters
[P in keyof GetStateAndGettersKey<D[K][AK], K>]: GetStateAndGettersKey<D[K][AK], K>[P]
}
}[StringKeyof<D>]>
type GetDispatchAndCommitWithThis<A, D extends Deps, AK extends 'actions' | 'mutations'> = (<T extends keyof GetAllDepsType<A, D, AK>>(type: T, ...payload: GetAllDepsType<A, D, AK>[T] extends (...payload: infer P) => any ? P : never) => GetAllDepsType<A, D, AK>[T] extends (...payload: any[]) => infer R ? R : never)

type GetDispatchAndCommitWithThis<A, D extends Deps, AK extends 'actions' | 'mutations'> = (<T extends keyof GetAllActionsKey<A, D, AK>>(type: T, ...payload: GetAllActionsKey<A, D, AK>[T] extends (...payload: infer P) => any ? P : never) => GetAllActionsKey<A, D, AK>[T] extends (...payload: any[]) => infer R ? R : never)
type GetAllMapKeys<S, D extends Deps, SK extends 'state' | 'getters'> = GetAllDepsType<S, D, SK> & GetStateAndGettersKey<S>

interface StoreOptWithThis<S, G, M, A, D extends Deps> {
state?: S
getters?: G & ThisType<{ state: S & UnboxDepsField<D, 'state'>, getters: GetComputedType<G> & UnboxDepsField<D, 'getters'>, rootState: any }>
mutations?: M & ThisType<{ state: S & UnboxDepsField<D, 'state'> }>
actions?: A & ThisType<{
rootState: any,
state: S & UnboxDepsField<D, 'state'>,
getters: GetComputedType<G> & UnboxDepsField<D, 'getters'>,
dispatch: GetDispatchAndCommitWithThis<A, D, 'actions'>,
commit: GetDispatchAndCommitWithThis<M, D, 'mutations'> & GetCommit<M, D>
} & { // todo elegant overload
dispatch(type: string, ...payload: any[]): any
commit(type: string, ...payload: any[]): any
}>
deps?: D
modules?: Record<string, StoreOptWithThis<{}, {}, {}, {}, {}>>
}

interface StoreWithThis<S = {}, G = {}, M = {}, A = {}, D extends Deps = {}> {
interface IStoreWithThis<S = {}, G = {}, M = {}, A = {}, D extends Deps = {}> {

state: S & UnboxDepsField<D, 'state'>
getters: GetComputedType<G> & UnboxDepsField<D, 'getters'>
Expand All @@ -117,11 +180,15 @@ declare namespace MpxStore {
mapState<K extends keyof S>(maps: K[]): {
[I in K]: () => S[I]
}
mapState(depPath: string, maps: string[]): {
[key: string]: () => any
mapState<T extends string, P extends string>(depPath: P, maps: readonly T[]): {
[K in T]: () => (CombineStringKey<P, K> extends keyof GetAllMapKeys<S, D, 'state'> ? GetAllMapKeys<S, D, 'state'>[CombineStringKey<P, K>] : any)
}
mapState<T extends mapStateFunctionType<S & UnboxDepsField<D, 'state'>, GetComputedType<G> & UnboxDepsField<D, 'getters'>>>(obj: ThisType<any> & T): {
[I in keyof T]: ReturnType<T[I]>
[I in keyof T]: () => ReturnType<T[I]>
}
// Support chain derivation
mapState<T extends { [key: string]: keyof GetAllMapKeys<S, D, 'state'> }>(obj: T): {
[I in keyof T]: () => GetAllMapKeys<S, D, 'state'>[T[I]]
}
mapState<T extends { [key: string]: keyof S }>(obj: T): {
[I in keyof T]: () => S[T[I]]
Expand All @@ -131,19 +198,25 @@ declare namespace MpxStore {
}

mapGetters<K extends keyof G>(maps: K[]): Pick<G, K>
mapGetters(depPath: string, maps: string[]): {
[key: string]: () => any
mapGetters<T extends string, P extends string>(depPath: P, maps: readonly T[]): {
// use GetComputedType to get getters' returns
[K in T]: () => (CombineStringKey<P, K> extends keyof GetAllMapKeys<GetComputedType<G>, D, 'getters'> ? GetAllMapKeys<GetComputedType<G>, D, 'getters'>[CombineStringKey<P, K>] : any)
}
// Support chain derivation
mapGetters<T extends { [key: string]: keyof GetAllMapKeys<GetComputedType<G>, D, 'getters'> }>(obj: T): {
[I in keyof T]: () => GetAllMapKeys<GetComputedType<G>, D, 'getters'>[T[I]]
}
mapGetters<T extends { [key: string]: keyof G }>(obj: T): {
[I in keyof T]: G[T[I]]
}
// When importing js in ts file, use this method to be compatible
mapGetters<T extends { [key: string]: string }>(obj: T): {
[I in keyof T]: (...payloads: any[]) => any
}

mapMutations<K extends keyof M>(maps: K[]): Pick<M, K>
mapMutations(depPath: string, maps: string[]): {
[key: string]: (...payloads: any[]) => any
mapMutations<T extends string, P extends string>(depPath: P, maps: readonly T[]): {
[K in T]: CombineStringKey<P, K> extends keyof GetAllDepsType<M, D, 'mutations'> ? GetAllDepsType<M, D, 'mutations'>[CombineStringKey<P, K>] : (...payloads: any[]) => any
}
mapMutations<T extends { [key: string]: keyof M }>(obj: T): {
[I in keyof T]: M[T[I]]
Expand All @@ -153,66 +226,20 @@ declare namespace MpxStore {
}

mapActions<K extends keyof A>(maps: K[]): Pick<A, K>
mapActions(depPath: string, maps: string[]): {
[key: string]: (...payloads: any[]) => any
mapActions<T extends string, P extends string>(depPath: P, maps: readonly T[]): {
[K in T]: CombineStringKey<P, K> extends keyof GetAllDepsType<A, D, 'actions'> ? GetAllDepsType<A, D, 'actions'>[CombineStringKey<P, K>] : (...payloads: any[]) => any
}
mapActions<T extends { [key: string]: keyof A }>(obj: T): {
[I in keyof T]: A[T[I]]
}
mapActions<T extends { [key: string]: string }>(obj: T): {
[I in keyof T]: (...payloads: any[]) => any
}

}

interface StoreOptWithThis<S, G, M, A, D extends Deps> {
state?: S
getters?: G & ThisType<{ state: S & UnboxDepsField<D, 'state'>, getters: GetComputedType<G> & UnboxDepsField<D, 'getters'>, rootState: any }>
mutations?: M & ThisType<{ state: S & UnboxDepsField<D, 'state'> }>
actions?: A & ThisType<{
rootState: any,
state: S & UnboxDepsField<D, 'state'>,
getters: GetComputedType<G> & UnboxDepsField<D, 'getters'>,
dispatch: GetDispatchAndCommitWithThis<A, D, 'actions'>,
commit: GetDispatchAndCommitWithThis<M, D, 'mutations'>
}>
deps?: D
modules?: Record<string, StoreOptWithThis<{}, {}, {}, {}, {}>>
}

interface Store<S = {}, G = {}, M = {}, A = {}, D extends Deps = {}> {

state: S & UnboxDepsField<D, 'state'>
getters: GetGetters<G> & UnboxDepsField<D, 'getters'>
mutations: GetMutations<M> & UnboxDepsField<D, 'mutations'>
actions: GetActions<A> & UnboxDepsField<D, 'actions'>

dispatch: GetDispatch<A, D>

commit: GetCommit<M, D>

mapState<K extends keyof S>(maps: K[]): {
[I in K]: () => S[I]
}
mapState(depPath: string, maps: string[]): object

mapGetters<K extends keyof G>(maps: K[]): {
[I in K]: () => GetGetters<G>[I]
}
mapGetters(depPath: string, maps: string[]): {
[key: string]: () => any
}

mapMutations<K extends keyof M>(maps: K[]): Pick<GetMutations<M>, K>
mapMutations(depPath: string, maps: string[]): {
[key: string]: (...payloads: any[]) => any
}

mapActions<K extends keyof A>(maps: K[]): Pick<GetActions<A>, K>
mapActions(depPath: string, maps: string[]): {
[key: string]: (...payloads: any[]) => any
}

type StoreWithThis<S = {}, G = {}, M = {}, A = {}, D extends Deps = {}> = IStoreWithThis<S, G, M, A, D> & {
dispatch(type: string, ...payload: any[]): any
commit(type: string, ...payload: any[]): any
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这里这样写主要是因为使用dispatch: () => {}这种形式定义的方法在ts中无法在同一个对象里面进行重载。

报错: Duplicate identifier 'dispatch'.ts(2300)

如果想要重载现在看来只能采取这种迂回的方式。

}

interface StoreOpt<S, G, M, A, D extends Deps> {
Expand Down