Skip to content

Instantly share code, notes, and snippets.

@ebrahimmfadae
Last active February 20, 2023 02:07
Show Gist options
  • Save ebrahimmfadae/1d1da46b176e7c2ffa56f0e91b801e8d to your computer and use it in GitHub Desktop.
Save ebrahimmfadae/1d1da46b176e7c2ffa56f0e91b801e8d to your computer and use it in GitHub Desktop.
Pure typescript solution to infer `params` and `meta` types in `call` and `mcall`
import {
CallingOptions,
Context,
MCallCallingOptions,
MCallDefinition,
ServiceActionsSchema,
ServiceBroker,
} from 'moleculer'
import { AllActionsUnion } from './actions'
type Resolve<T> = T extends PromiseLike<infer R> ? R : T
type RemoveIndex<T> = {
[K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]
}
type ContextParam<T> = T extends Context<infer P> ? P : unknown
type ActionContext<T extends keyof AllActionsUnion> = Parameters<AllActionsUnion[T]>[0]
type XMCallDefinition<T extends string> = T extends keyof AllActionsUnion
? Omit<Partial<MCallDefinition<ContextParam<ActionContext<T>>>>, 'action'> & { action: T }
: never
type ActionReturnType<T extends keyof AllActionsUnion> = Resolve<ReturnType<AllActionsUnion[T]>>
type XCallingOptions<T> = T extends Context<any, infer M>
? CallingOptions & { meta?: Partial<M> }
: CallingOptions
type ExtendCallMethod<T extends keyof AllActionsUnion = keyof AllActionsUnion> = {
call<K extends T>(actionName: K): Promise<ActionReturnType<K>>
call<K extends T>(
actionName: K,
params: ContextParam<ActionContext<K>>,
opts?: XCallingOptions<ActionContext<K>>,
): Promise<ActionReturnType<K>>
mcall<U extends Record<string, XMCallDefinition<T>>>(
def: U,
opts?: MCallCallingOptions,
): Promise<{ [K in keyof U]: ActionReturnType<U[K]['action']> }>
mcall<U extends XMCallDefinition<keyof AllActionsUnion>[]>(
def: [...U],
opts?: MCallCallingOptions,
): Promise<{ [K in keyof U]: ActionReturnType<U[K]['action']> }>
}
type XServiceBroker = Omit<RemoveIndex<ServiceBroker>, 'call' | 'mcall'> & ExtendCallMethod
type XContext<P = unknown, M extends object = {}> = Omit<
Context<P, M>,
'call' | 'mcall' | 'broker'
> &
ExtendCallMethod & { broker: XServiceBroker }
export const createActions = <T extends ServiceActionsSchema>(actions: T) => actions
export const xContext = <P, M extends object>(ctx: Context<P, M>) => ctx as XContext<P, M>
export const xBroker = (broker: ServiceBroker) => broker as XServiceBroker
import { ActionSchema, ServiceActionsSchema } from 'moleculer'
import type * as HealthCheck from './services/healthCheck.service'
type ServiceMap<S extends ServiceActionsSchema, V extends string, N extends string> = {
[K in keyof S as `${V}.${N}.${K & string}`]: S[K] extends ActionSchema ? S[K]['handler'] : S[K]
}
export type AllActionsUnion = ServiceMap<HealthCheck.Actions, HealthCheck.Version, HealthCheck.Name>
import { createActions } from '../x.moleculer'
const name = 'healthCheck'
const version = 1
const actions = createActions({
check() {
return 'Works'
},
})
/**
* You can declare actions using satisfies keyword in Typescript 4.9+
*
* const actions = {
* check() {
* return 'Works'
* },
* } satisfies ServiceActionsSchema
*/
export type Actions = typeof actions
export type Name = typeof name
export type Version = `v${typeof version}`
xContext(ctx).call('v1.healthCheck.check')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment