diff --git a/index.d.ts b/index.d.ts index 03f295c41..1345e3e2b 100644 --- a/index.d.ts +++ b/index.d.ts @@ -44,7 +44,16 @@ declare namespace Moleculer { trace(...args: any[]): void; } - type ActionHandler = ((ctx: Context) => Promise | T) & ThisType; + type ActionHandler< + F extends ActionsMappingFunction = never, + ActionsMapping extends ActionsMappingBase = never, + EventsMapping extends EventsMappingBase = never + > = [F] extends [never] + ? ((ctx: Context) => Promise | any) & + ThisType + : ((ctx: Context[0], any, ActionsMapping, EventsMapping>) => Promise> | ReturnType) & + ThisType; + type ActionParamSchema = { [key: string]: any }; type ActionParamTypes = | "any" @@ -446,13 +455,17 @@ declare namespace Moleculer { error?: string | ActionHookError | Array; } - interface ActionSchema { + type ActionSchema< + F extends ActionsMappingFunction = never, + ActionsMapping extends ActionsMappingBase = never, + EventsMapping extends EventsMappingBase = never + > = { name?: string; visibility?: ActionVisibility; params?: ActionParams; service?: Service; cache?: boolean | ActionCacheOptions; - handler?: ActionHandler; + handler?: ActionHandler; tracing?: boolean | TracingActionOptions; bulkhead?: BulkheadOptions; circuitBreaker?: BrokerCircuitBreakerOptions; @@ -460,8 +473,16 @@ declare namespace Moleculer { fallback?: string | FallbackHandler; hooks?: ActionHooks; - [key: string]: any; - } + // See https://github.com/moleculerjs/moleculer/issues/467#issuecomment-705583471 + [key: string]: + | string + | boolean + | any[] + | number + | Record + | null + | undefined; + }; interface EventSchema { name?: string; @@ -476,7 +497,35 @@ declare namespace Moleculer { [key: string]: any; } - type ServiceActionsSchema = { [key: string]: ActionSchema | ActionHandler | boolean; }; + // Resolves to the first member of a tuple + type First = T extends [infer U, ...(infer _)] ? U : never; + + // Resolves to Default if any of the types in Tests are never. + // Otherwise, resolves to "ok" + type DefaultIfNever = Tests extends [] + ? "ok" + : First extends never + ? Default + : Tests extends [any, ...(infer Rest)] + ? DefaultIfNever + : Default; + + type ServiceActionsSchema< + ActionsMapping extends ActionsMappingBase = never, + ActionsEntry extends keyof ActionsMapping = never, + EventsMapping extends EventsMappingBase = never, + > = [DefaultIfNever] extends [never] + ? { + [key: string]: + | ActionHandler + | ActionSchema + | boolean; + } : { + [P in keyof ActionsMapping[ActionsEntry]]?: + | ActionHandler + | ActionSchema + | boolean; + }; class BrokerNode { id: string; @@ -506,17 +555,82 @@ declare namespace Moleculer { disconnected(): void; } - class Context

{ - constructor(broker: ServiceBroker, endpoint: Endpoint); + // Removes a level inside the object of objects by concatenating the keys. + // Requires TypeScript >= 4.1 + type ConcatenateProps = { + [P in keyof A[K] & string as `${K}.${P}`]: A[K][P]; + }; + + // Converts a type union into a type intersection + type UnionToIntersect = (T extends any ? ((x: T) => 0) : never) extends ((x: infer R) => 0) ? R : never; + + // Return the intersection of each value of properties of T. + type IntersectItems = UnionToIntersect; + + // Make sure the functions always return promises. + type CallerReturnType = { + [P in keyof T & string]: (...args: Parameters) => Promise> + }; + + // CallableActions is the interface where every action is displayed using its fullname. + type CallableActions = IntersectItems<{ + [Svc in keyof A & string]: CallerReturnType>; + }> + + // Helpers to filter based on the type of the values in an interface. + type KeysOfType = K extends (T[K] extends Filter ? K : never) ? K : never; + type MembersOfType = Pick>; + type MembersNotOfType = Omit>; + + // Helpers to include or omit the payload depending on the number of parameters the function expects. + type CallWithoutPayload any>> = (actionName: K) => ReturnType; + type CallWithPayload any>> = (actionName: K, params: Parameters[0], opts?: CallingOptions) => ReturnType; + + // Units all overloards for the function + type Call< + A extends ActionsMappingBase, + CActions extends CallableActions = CallableActions, + > = + CallWithoutPayload any>> & + CallWithPayload any>>; + + // Not strictly typed call function + type LegacyCall = ( + ((actionName: string) => Promise) & + ((actionName: string, params: P, opts?: CallingOptions) => Promise) + ); + + type EmitWithoutPayload> = (eventName: keyof A) => Promise; + type EmitWithPayload> = (eventName: K, data: A[K]) => Promise; + + type Emit = EmitWithoutPayload> & EmitWithPayload>; + + // Not strictly typed call function + type LegacyEmit = ( + ((eventName: string, data: D, opts: GenericObject) => Promise) & + ((eventName: string, data: D, groups: Array) => Promise) & + ((eventName: string, data: D, groups: string) => Promise) & + ((eventName: string, data: D) => Promise) & + ((eventName: string) => Promise) + ); + + + class Context< + P = unknown, + M extends object = {}, + ActionsMapping extends ActionsMappingBase = never, + EventsMapping extends EventsMappingBase = never, + > { + constructor(broker: ServiceBroker, endpoint: Endpoint); id: string; - broker: ServiceBroker; + broker: ServiceBroker; endpoint: Endpoint | null; action: ActionSchema | null; event: EventSchema | null; service: Service | null; nodeID: string | null; - eventName: string | null; + eventName: ([EventsMapping] extends [never] ? string : keyof EventsMapping) | null; eventType: string | null; eventGroups: Array | null; @@ -544,22 +658,15 @@ declare namespace Moleculer { setEndpoint(endpoint: Endpoint): void; setParams(newParams: P, cloning?: boolean): void; - call(actionName: string): Promise; - call(actionName: string, params: P, opts?: CallingOptions): Promise; - mcall(def: Array | { [name: string]: MCallDefinition }, opts?: CallingOptions): Promise | T>; + call: [ActionsMapping] extends [never] ? LegacyCall : Call; + emit: [EventsMapping] extends [never] ? LegacyEmit : Emit; + broadcast: [EventsMapping] extends [never] ? LegacyEmit : Emit; - emit(eventName: string, data: D, opts: GenericObject): Promise; - emit(eventName: string, data: D, groups: Array): Promise; - emit(eventName: string, data: D, groups: string): Promise; - emit(eventName: string, data: D): Promise; - emit(eventName: string): Promise; - - broadcast(eventName: string, data: D, opts: GenericObject): Promise; - broadcast(eventName: string, data: D, groups: Array): Promise; - broadcast(eventName: string, data: D, groups: string): Promise; - broadcast(eventName: string, data: D): Promise; - broadcast(eventName: string): Promise; + mcall( + def: Array | { [name: string]: MCallDefinition }, + opts?: CallingOptions, + ): Promise | T>; copy(endpoint: Endpoint): this; copy(): this; @@ -584,21 +691,61 @@ declare namespace Moleculer { [name: string]: any; } - type ServiceEventLegacyHandler = ((payload: any, sender: string, eventName: string, ctx: Context) => void) & ThisType; - - type ServiceEventHandler = ((ctx: Context) => void) & ThisType; - - interface ServiceEvent { + type ServiceEventLegacyHandler< + EventsMapping extends EventsMappingBase = never, + EventName extends keyof EventsMapping = never, + ActionsMapping extends ActionsMappingBase = never + > = [DefaultIfNever] extends [never] + ? (( + payload: any, + sender: string, + eventName: string, + ctx: Context, + ) => void) & ThisType + : (( + payload: EventsMapping[EventName], + sender: string, + eventName: EventName, + ctx: Context, + ) => void) & ThisType; + + type ServiceEventHandler< + EventsMapping extends EventsMappingBase = never, + EventName extends keyof EventsMapping = never, + ActionsMapping extends ActionsMappingBase = never + > = [DefaultIfNever] extends [never] + ? ((ctx: Context) => void) & ThisType + : ((ctx: Context) => void) & ThisType; + + type ServiceEvent< + EventsMapping extends EventsMappingBase = never, + EventName extends keyof EventsMapping = never, + ActionsMapping extends ActionsMappingBase = never + > = { name?: string; group?: string; params?: ActionParams; context?: boolean; debounce?: number; throttle?: number; - handler?: ServiceEventHandler | ServiceEventLegacyHandler; + handler?: + | ServiceEventHandler + | ServiceEventLegacyHandler; } - type ServiceEvents = { [key: string]: ServiceEventHandler | ServiceEventLegacyHandler | ServiceEvent }; + type ServiceEvents< + EventsMapping extends EventsMappingBase = never, + ActionsMapping extends ActionsMappingBase = never + > = [EventsMapping] extends [never] + ? { + [key: string]: ServiceEventHandler | ServiceEventLegacyHandler | ServiceEvent; + } + : { + [P in keyof EventsMapping]?: + | ServiceEventHandler + | ServiceEventLegacyHandler + | ServiceEvent + }; type ServiceMethods = { [key: string]: ((...args: any[]) => any) } & ThisType; @@ -652,18 +799,30 @@ declare namespace Moleculer { version?: string | number; } - interface ServiceSchema { + // Types used to represent the mapping and its components + type ActionsMappingFunction = ((params: any) => unknown) | (() => unknown); + type ActionsMappingChildren = Record; + type ActionsMappingBase = Record; + + type EventsMappingBase = Record; + + interface ServiceSchema< + S = ServiceSettingSchema, + ActionsMapping extends ActionsMappingBase = never, + ActionsEntry extends keyof ActionsMapping = never, + EventsMapping = never, + > { name: string; version?: string | number; - settings?: S; + settings?: [S] extends [never] ? ServiceSettingSchema : S; dependencies?: string | ServiceDependency | Array; metadata?: GenericObject; - actions?: ServiceActionsSchema; + actions?: ServiceActionsSchema; mixins?: Array>; methods?: ServiceMethods; hooks?: ServiceHooks; - events?: ServiceEvents; + events?: ServiceEvents; created?: (() => void) | Array<() => void>; started?: (() => Promise) | Array<() => Promise>; stopped?: (() => Promise) | Array<() => Promise>; @@ -677,10 +836,15 @@ declare namespace Moleculer { [name: string]: ServiceAction; } - class Service implements ServiceSchema { - constructor(broker: ServiceBroker, schema?: ServiceSchema); + class Service< + S = ServiceSettingSchema, + ActionsMapping extends ActionsMappingBase = never, + ActionsEntry extends keyof ActionsMapping = never, + EventsMapping extends EventsMappingBase = never, + > implements ServiceSchema { + constructor(broker: ServiceBroker, schema?: ServiceSchema); - protected parseServiceSchema(schema: ServiceSchema): void; + protected parseServiceSchema(schema: ServiceSchema): void; name: string; fullName: string; @@ -688,9 +852,9 @@ declare namespace Moleculer { settings: S; metadata: GenericObject; dependencies: string | ServiceDependency | Array; - schema: ServiceSchema; - originalSchema: ServiceSchema; - broker: ServiceBroker; + schema: ServiceSchema; + originalSchema: ServiceSchema; + broker: ServiceBroker; logger: LoggerInstance; actions: ServiceActions; Promise: PromiseConstructorLike; @@ -958,8 +1122,10 @@ declare namespace Moleculer { removeIfExist(command:string): void; } - - class ServiceBroker { + class ServiceBroker< + ActionsMapping extends ActionsMappingBase = never, + EventsMapping extends EventsMappingBase = never + > { constructor(options?: BrokerOptions); options: BrokerOptions; @@ -1022,28 +1188,15 @@ declare namespace Moleculer { findNextActionEndpoint(actionName: string, opts?: GenericObject, ctx?: Context): ActionEndpoint | Errors.MoleculerRetryableError; - call(actionName: string): Promise; - call(actionName: string, params: P, opts?: CallingOptions): Promise; - - mcall(def: Array | { [name: string]: MCallDefinition }, opts?: CallingOptions): Promise | T>; - - emit(eventName: string, data: D, opts: GenericObject): Promise; - emit(eventName: string, data: D, groups: Array): Promise; - emit(eventName: string, data: D, groups: string): Promise; - emit(eventName: string, data: D): Promise; - emit(eventName: string): Promise; - - broadcast(eventName: string, data: D, opts: GenericObject): Promise; - broadcast(eventName: string, data: D, groups: Array): Promise; - broadcast(eventName: string, data: D, groups: string): Promise; - broadcast(eventName: string, data: D): Promise; - broadcast(eventName: string): Promise; + call: [ActionsMapping] extends [never] ? LegacyCall : Call; + emit: [EventsMapping] extends [never] ? LegacyEmit : Emit; + broadcast: [EventsMapping] extends [never] ? LegacyEmit : Emit; + broadcastLocal: [EventsMapping] extends [never] ? LegacyEmit : Emit; - broadcastLocal(eventName: string, data: D, opts: GenericObject): Promise; - broadcastLocal(eventName: string, data: D, groups: Array): Promise; - broadcastLocal(eventName: string, data: D, groups: string): Promise; - broadcastLocal(eventName: string, data: D): Promise; - broadcastLocal(eventName: string): Promise; + mcall( + def: Array | { [name: string]: MCallDefinition }, + opts?: CallingOptions, + ): Promise | T>; ping(): Promise; ping(nodeID: string | Array, timeout?: number): Promise; diff --git a/package-lock.json b/package-lock.json index d0345a941..f71081ca4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14812,9 +14812,9 @@ } }, "typescript": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.3.tgz", - "integrity": "sha512-tEu6DGxGgRJPb/mVPIZ48e69xCn2yRmCgYmDugAVwmJ6o+0u1RI18eO7E7WBTLYLaEVVOhwQmcdhQHweux/WPg==", + "version": "4.1.0-beta", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.0-beta.tgz", + "integrity": "sha512-b/LAttdVl3G6FEmnMkDsK0xvfvaftXpSKrjXn+OVCRqrwz5WD/6QJOiN+dTorqDY+hkaH+r2gP5wI1jBDmdQ7A==", "dev": true }, "unc-path-regex": { diff --git a/package.json b/package.json index 60a02dd67..974ea218b 100644 --- a/package.json +++ b/package.json @@ -101,7 +101,7 @@ "thrift": "^0.12.0", "ts-node": "^9.0.0", "tsd": "^0.13.1", - "typescript": "^4.0.3", + "typescript": "^4.1.0-beta", "v8-natives": "^1.2.0", "winston": "^3.3.3", "winston-context": "^0.0.7"