Is your feature request related to a problem? Please describe.
The current type definitions does not provide any type-safety regarding ctx.call, ctx.emit, actions definitions in service and events handlers in services.
Describe the solution you'd like
We could use several meta-types and a user-defined mapping allowing us to properly type everything.
In particular, I was thinking about the following for user-defined mappings related to actions:
type ServiceActions = {
"v1.chat": {
get: (arg: { toto: string }) => boolean;
list: () => number;
};
"v1.characters": {
get: (arg: { id: string }) => string;
};
};
The meaning of this type is:
- We have two services available:
v1.chat and v1.characters.
- The service
v1.chat defines two actions: get and list
- The service
v1.characters defines one action get
- The action
v1.chat.get takes a parameter containing toto: string and returns a boolean
- The action
v1.chat.list takes no parameter and returns a number
- The action
v1.characters.get takes a parameter containing id: string and returns a string
Given this mapping, we have every information we need.
I thought about using it the following way (take a look at the inheritance):
class ChatService extends Service<ServiceActions, "v1.chat"> {
constructor(broker: ServiceBroker) {
super(broker);
this.parseServiceSchema({
name: "chat",
version: "v1",
actions: {
async get(ctx) {
const str = await ctx.call("v1.characters.get"); // Type error: `id: string` was expected as parameter
return ctx.params.toto; // Triggers a type error because it should return a boolean
},
list: {
handler(ctx) {
return "plop"; // Triggers a type error because it should return a number
},
},
},
});
}
}
To properly type events, we also rely on a mapping of the following form:
type ServiceEvents = {
"event1": { id: string };
};
Meaning "There is one event event1 and its payload is an object containing an id of type string."
We don't need anything else in order to have strongly-types events. This mapping would need to be passed as a third template parameter to the Service class, though.
Describe alternatives you've considered
I haven't considered any alternative, this is the only proper solution I could think of that would cover all my requirements.
Additional context
A potential issue with the implemention I think about is that it relies on TypeScript 4.1 (Specifically this issue). This is used to generate v1.characters.get from the keys of the mapping.
This feature is a lifesaver since it lets us stay DRY (In the action definition we don't use the service name but we use it in the action call)
I've already started working on the implementation because I think it's mandatory when using such a library in TypeScript.
I currently have the action implementation strongly typed and am working on ctx.call.
I can show the code and explain everything if it is relevant.
The following code was the proof of concept used to find the way to go, if someone is curious:
interface Actions {
"v1.auth": {
get: (toto: string) => boolean;
list: () => number;
};
"v1.characters": {
get: (id: string) => string;
}
}
type Func = (...arg...
Playground Link
Just to clarify: I intend to implement this. I just thought I could open an issue to discuss the used interface because it might be useful for people. It could become a PR if the solution I implement is satisfying for everyone.
And I stumbled on this "problem" while tinkering: #467 (comment). It's not a problem without this feature but gets in the way of the implementation I'm working on.
Is your feature request related to a problem? Please describe.
The current type definitions does not provide any type-safety regarding
ctx.call,ctx.emit, actions definitions in service and events handlers in services.Describe the solution you'd like
We could use several meta-types and a user-defined mapping allowing us to properly type everything.
In particular, I was thinking about the following for user-defined mappings related to actions:
The meaning of this type is:
v1.chatandv1.characters.v1.chatdefines two actions:getandlistv1.charactersdefines one actiongetv1.chat.gettakes a parameter containingtoto: stringand returns abooleanv1.chat.listtakes no parameter and returns anumberv1.characters.gettakes a parameter containingid: stringand returns astringGiven this mapping, we have every information we need.
I thought about using it the following way (take a look at the inheritance):
To properly type events, we also rely on a mapping of the following form:
Meaning "There is one event
event1and its payload is an object containing anidof typestring."We don't need anything else in order to have strongly-types events. This mapping would need to be passed as a third template parameter to the
Serviceclass, though.Describe alternatives you've considered
I haven't considered any alternative, this is the only proper solution I could think of that would cover all my requirements.
Additional context
A potential issue with the implemention I think about is that it relies on TypeScript 4.1 (Specifically this issue). This is used to generate
v1.characters.getfrom the keys of the mapping.This feature is a lifesaver since it lets us stay DRY (In the action definition we don't use the service name but we use it in the action call)
I've already started working on the implementation because I think it's mandatory when using such a library in TypeScript.
I currently have the action implementation strongly typed and am working on
ctx.call.I can show the code and explain everything if it is relevant.
The following code was the proof of concept used to find the way to go, if someone is curious:
Playground Link
Just to clarify: I intend to implement this. I just thought I could open an issue to discuss the used interface because it might be useful for people. It could become a PR if the solution I implement is satisfying for everyone.
And I stumbled on this "problem" while tinkering: #467 (comment). It's not a problem without this feature but gets in the way of the implementation I'm working on.