UNPKG

cumqueoptio

Version:

The meta-framework suite designed from scratch for frontend-focused modern web development.

263 lines (236 loc) 6.99 kB
import { Context } from './app'; export interface ReduxAction<Payload = any> { type: string; payload: Payload; extraArgs: any[]; } export type Action<State, Payload = any> = ( state: State, payload: Payload, ...extraArgs: any[] ) => State | void; export interface Actions<State> { [key: string]: Action<State> | Actions<State>; } // eslint-disable-next-line @typescript-eslint/no-empty-interface export interface ModelDescOptions {} // eslint-disable-next-line @typescript-eslint/no-unused-vars export interface ModelDesc<State = any, MDO extends ModelDescOptions = any> { /** * Unique name */ name: string; /** * Model state */ state: State; /** * Model computed properties */ computed?: Computed<State>; /** * Model actions, it's a pure function with no effect */ actions?: Actions<State>; } export interface DispatchActions { [key: string]: ((payload: any) => ReduxAction) | DispatchActions; } export type Model = { (name: string): any; _: Omit<ModelDesc, 'name'>; // model name _name: string; }; type Merge<T extends Record<string, any>, K extends string> = { [key in keyof T as K]: T[key]; }; type UnionToIntersection<U> = ( U extends any ? (arg: U) => any : never ) extends (arg: infer I) => void ? I : never; /** * GetActions * A type hook to resolve actions type from Model */ export interface GetActions<M extends Model> { actions: ExtractDispatchActions<M['_']['actions']>; } /** * GetState * A type hook to resolve state type from Model */ export interface GetState<M extends Model> { state: M['_']['state']; } export interface Computed<State> { [key: string]: | ((state: State) => any) | [...s: Model[], selector: (state: State, ...args: any) => any]; } type GetComputedFromArray<A> = A extends { _: object } ? never : A extends (...args: any) => infer R ? R : any; /** * GetComputed * A type hook to resolve computed type from Model */ export interface GetComputed<M extends Model, C = M['_']['computed']> { computed: { [K in keyof C]: C[K] extends (...args: any[]) => infer R ? R : C[K] extends Array<infer A> ? GetComputedFromArray<A> : K; }; } export type GetModelState<M extends Model = Model> = GetComputed<M>['computed'] extends Record<string, any> ? UnionToIntersection< | Merge<GetState<M>, 'state'>['state'] | Merge<GetComputed<M>, 'computed'>['computed'] > : Merge<GetState<M>, 'state'>['state']; export type GetModelActions<M extends Model = Model> = UnionToIntersection< Merge<GetActions<M>, 'actions'>['actions'] >; export type MountedModel<M extends Model = Model> = { name: string; state: GetModelState<M>; actions: GetModelActions<M>; modelDesc: ModelDesc; }; /** * Extract action dispatcher from model actions define info * @example * * before * { * actions: { * a: (state: State, payload: string) => {} * } * } * * after * { * actions: { * a: (payload: string) => {}; * } * } */ type ExtractDispatchActions<A extends Actions<any> = Actions<any>> = { [key in keyof A]: A[key] extends Actions<any> ? ExtractDispatchActions<A[key]> : A[key] extends () => any ? () => ReduxAction<undefined> : A[key] extends (S: any, ...args: infer S) => any ? (...args: S) => ReduxAction<S[0]> : never; }; type ModelType = Model; // eslint-disable-next-line @typescript-eslint/ban-types type ExcludeEmpty<T> = T extends infer P & {} ? P : T; type NeededUnionType<T extends any[]> = T[number]; type GetModelsState<Models extends Model[]> = ExcludeEmpty< UnionToIntersection< NeededUnionType<{ [Index in keyof Models]: MountedModel<Models[Index]>['state']; }> > >; type GetModelsAction<Models extends Model[]> = Models extends [ infer Head, ...infer Tail, ] ? (Head extends Model ? UnionToIntersection<Merge<GetActions<Head>, 'actions'>['actions']> : Record<string, unknown>) & (Tail extends Model[] ? GetModelsAction<Tail> : Record<string, unknown>) : Record<string, unknown>; type GetModelsStateTuple< Models extends ModelType[], U extends any[] = [], > = Models extends [infer Head, ...infer Tail] ? Head extends ModelType ? Tail extends ModelType[] ? GetModelsStateTuple<Tail, [...U, GetModelsState<[Head]>]> : [...U, GetModelsState<[Head]>] : U : U; type GetModelsActionsTuple< Models extends ModelType[], U extends any[] = [], > = Models extends [infer Head, ...infer Tail] ? Head extends ModelType ? Tail extends ModelType[] ? GetModelsActionsTuple<Tail, [...U, GetModelsAction<[Head]>]> : [...U, GetModelsAction<[Head]>] : U : U; export interface UseModel { // useModel(aModel, bModel); <Models extends Model[]>(...models: Models): [ GetModelsState<Models>, GetModelsAction<Models>, ReturnType<Context['apis']['getModelSubscribe']>, ]; // useModel([AModel, BModel]); <Models extends Model[]>(models: [...Models]): [ GetModelsState<Models>, GetModelsAction<Models>, ReturnType<Context['apis']['getModelSubscribe']>, ]; // useModel(Amodel, BModel, ..., stateSelector, actionsSelector) < Models extends Model[], StateSelector extends (...args: GetModelsStateTuple<Models>) => any, ActionSelector extends (...args: GetModelsActionsTuple<Models>) => any, >( ...arg: [...models: Models, s?: StateSelector, a?: ActionSelector] ): [ StateSelector extends (...args: any[]) => infer S ? S : never, ActionSelector extends (...args: any[]) => infer S ? S : never, ReturnType<Context['apis']['getModelSubscribe']>, ]; // useModel(Amodel, BModel, ..., stateSelector) < Models extends Model[], StateSelector extends (...args: GetModelsStateTuple<Models>) => any, >( ...models: [...Models, StateSelector] ): [ StateSelector extends (...args: any[]) => infer S ? S : never, GetModelsAction<Models>, ReturnType<Context['apis']['getModelSubscribe']>, ]; // useModel([Amodel, BModel, ...], stateSelector, actionsSelector) < Models extends Model[], StateSelector extends (...args: GetModelsStateTuple<Models>) => any, ActionSelector extends (...args: GetModelsActionsTuple<Models>) => any, >( models: [...Models], s?: StateSelector, a?: ActionSelector, ): [ StateSelector extends (...args: any[]) => infer S ? S : never, ActionSelector extends (...args: any[]) => infer S ? S : never, ReturnType<Context['apis']['getModelSubscribe']>, ]; // useModel([Amodel, BModel, ...], stateSelector) < Models extends Model[], StateSelector extends (...args: GetModelsStateTuple<Models>) => any, >( models: [...Models], s?: StateSelector, ): [ StateSelector extends (...args: any[]) => infer S ? S : never, GetModelsAction<Models>, ReturnType<Context['apis']['getModelSubscribe']>, ]; } export type OnMountHook = (handler: () => void) => void;