I'm trying to reduce amount of code and error by introducing static constraints to chat commands. Commands are described with a name and arguments. Here's what I've got:
type ArgumentTypeName = 'string' | 'number' | 'user';
type CommandArguments<T> = { [k in keyof T]: ArgumentTypeName };
type CommandHandler<T> = (args: T) => void;
type Command<T> = {
name: string;
arguments: CommandArguments<T>;
handler: CommandHandler<T>;
}
I can't figure out how to get T type from arguments of Command<T>, so it can correctly influence handler.
So far I'm getting a type with correct keys, but all values are unknown. Like this:
arguments = { arg1: 'string' }
// handler: (args: { arg1: unknown }) => void
I also tried to constraint arguments, thinking that if I don't specify T, it will be inferred from arguments, but that didnt work:
type ArgumentName<T> =
T extends string ? 'string' :
T extends number ? 'number' :
T extends User ? 'user' :
never;
type CommandArguments<T> = { [k in keyof T]: ArgumentName<T[k]> };
type CommandHandler<T> = (args: T) => void;
type Command<T> = {
name: string;
arguments: CommandArguments<T>;
handler: CommandHandler<T>;
}
How can I, like, infer-backwards T based on its property CommandArguments<T>?
So I get CommandArguments<T> → T → CommandHandler<T>?
Or is there a different approach that I didn't think of?
Edit: Example of code that uses this:
/*
In another file:
const commands: Command<any>[] = [];
export function createCommand<T>(cmd: Command<T>) {
commands.push(cmd);
}
*/
createCommand({
name: 'add',
arguments: {
a: 'number',
b: 'number'
},
handler: (args) => {
// (parameter) args: { a: unknown; b: unknown; }
// Error: Object is of type 'unknown'. ts(2571)
console.log(args.a + args.b);
},
});