I am in the process of creating a typed function that will utilize an object based on its key:
const commandOrQuery = {
CREATE_USER_WITH_PASSWORD: CreateUserCommandHandler,
GET_USERS: GetUsersQueryHandler,
};
The structure of commandOrQuery
is as follows: Record<string, Function>
I intend to develop a dispatch
function that functions like this:
export const dispatch = ({type, args}) => commandOrQuery[type](args)
Here's how it works:
type
represents the key from the commandOrQuery
object.
args
correspond to the arguments of the commandOrquery[type]
function.
The dispatch
function then returns the type of the object.
For example, when I execute the following code:
const result = await dispatch({
type: "GET_USERS",
args : {
// tenantId is exclusive to "GET_USERS"
tenantId: string;
}
});
// The result would have the shape {name: string, id: string}[] specific to "GET_USERS" and GetUsersQueryHandler
I've made progress by defining the types separately like so:
export type Mediator =
| {
type: "CREATE_USER_WITH_PASSWORD";
arg: CreateUserCommand | typeof CreateUserCommandHandler;
}
| {
type: "GET_USERS";
arg: GetUsersQuery | typeof GetUsersQueryHandler;
}
| {
type: "GET_USER_PROFILE";
arg: GetUserProfile | typeof GetUserProfileQueryHandler;
};
And adjusting the dispatch implementation accordingly:
export const dispatch = ({type, args}: Mediator) => commandOrQuery[type](args)
However, the missing piece right now is the return type. My goal is for TypeScript to automatically infer the ReturnType once I provide the type
in the argument.
Is there a way to achieve this?
I've been exploring various resources for a couple of hours now: TypeScript conditional return value type? Typescript Key-Value relation preserving Object.entries type
Edit x1: Check out the Code Sandbox demo here: https://codesandbox.io/s/prod-http-50m9zk?file=/src/mediator.ts
Edit x2:
Let's consider the following file structure:
https://i.sstatic.net/vAS2g.png
//file: bounded_contexts/CreateUserWithPasswordCommand.ts
export type CreateUserCommand = {
email: string;
password: string;
};
export async function CreateUserCommandHandler(cmd: CreateUserCommand) {
console.log("Creating User");
return true;
}
// file: bounded_contexts/GetUserProfileQuery.ts
export type GetUserProfileQuery = {
userId: string;
};
export async function GetUserProfileQueryHandler(query: GetUserProfileQuery) {
console.log("Searching in db the userId", query.userId);
return {
firstName: "Carlos",
lastName: "Johnson",
dateOfBirth: new Date()
};
}
// file: bounded_contexts/GetUsersQuery.ts
export type GetUsersQuery = {};
export async function GetUsersQueryHandler(q: GetUsersQuery) {
return {
users: [
{
id: "id-1",
name: "Julian Perez"
},
{
id: "id-2",
name: "Adam Smith"
}
]
};
}
// file: index.ts
import { dispatch } from "./mediator";
(async () => {
// Result should be boolean
const result = await dispatch({
type: "CREATE_USER_WITH_PASSWORD",
arg: {
email: "<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="22564a47474f434b4e624f434b4e0c414d4f">[email protected]</a>",
password: "the password"
}
});
// result2 should be { users: {name: string; id: string }[]
const result2 = await dispatch({
type: "GET_USERS",
arg: {}
});
// resul3 should be { firstName: string; lastName: string; dateOfBirth: Date}
const result3 = await dispatch({
type: "GET_USER_PROFILE",
arg: {
userId: "the user Id"
}
});
})();
// file: mediator.ts
import {
CreateUserCommandHandler,
CreateUserCommand
} from "./bounded_contexts/CreateUserWithPasswordCommand";
import {
GetUsersQueryHandler,
GetUsersQuery
} from "./bounded_contexts/GetUsersQuery";
import {
GetUserProfileQueryHandler,
GetUserProfileQuery
} from "./bounded_contexts/GetUserProfileQuery";
const commandsOrQueries = {
CREATE_USER_WITH_PASSWORD: CreateUserCommandHandler,
GET_USERS: GetUsersQueryHandler,
GET_USER_PROFILE: GetUserProfileQueryHandler
};
type Mediator =
| {
type: "CREATE_USER_WITH_PASSWORD";
arg: CreateUserCommand | typeof CreateUserCommandHandler;
}
| {
type: "GET_USERS";
arg: GetUsersQuery | typeof GetUsersQueryHandler;
}
| {
type: "GET_USER_PROFILE";
arg: GetUserProfileQuery | typeof GetUserProfileQueryHandler;
};
export function dispatch({ arg, type }: Mediator) {
return commandsOrQueries[type](arg as any);
}