I have a legacy API as shown below (playground link)...
type Command1 = {
cmd: "my first command",
arg1: string,
arg2: boolean
}
type Command2 = {
cmd: "my second command",
foo: string,
bar: number
}
type Command = Command1 | Command2
function execute(cmd: Command["cmd"], args:any /* would like to strongly type this */) {
console.log(args)
}
execute("my first command", {/* oops missing props */})
Is there any way to type check the args
param of the execute
function without changing the parameter list of the function?
Thanks
CodePudding user response:
Use Extract<Type, Union> (playground):
function execute<Cmd extends Command["cmd"]>(
cmd: Cmd,
args: Omit<Extract<Command, { cmd: Cmd }>, "cmd">
) {
console.log(cmd, args)
}
// execute<"my first command">(cmd: "my first command", args: Omit<Command1, "cmd">): void
execute("my first command", { arg1: "ksd", arg2: true })
// execute<"my second command">(cmd: "my second command", args: Omit<Command2, "cmd">): void
execute("my second command", { foo: "qwe", bar: 123 })
CodePudding user response:
Perhaps you can try function overloading:
function execute(cmd: Command1["cmd"], args:Omit<Command1, 'cmd'>) : void;
function execute(cmd: Command2["cmd"], args:Omit<Command2, 'cmd'>) : void;
function execute(cmd: Command["cmd"], args:Omit<Command, 'cmd'>): void {
console.log(args);
}
execute("my first command", {arg1: '1', arg2: true})
execute("my first command", {foo: '1', bar: 2}) // <-- error
CodePudding user response:
How about using an intersection type with generics for this? I'm assuming the rest of the code is the same as your example:
function execute<T extends Command["cmd"]>(cmd: T, args: Command & { cmd: T }) {
console.log(cmd, args)
}
I can't think of a better way of "gluing" together the discriminator and the right type of Command
.