Start a subshell:
(umask 22 && cmd)
Then the new umask will only alter that subshell.
Note that zsh executes the last command of the subshell in the subshell process instead of forking another one, which means that if that command is external, you're not even wasting a process, it's just that the fork is done earlier (so the umask is done in the child process that will later execute your command).
In shells such as bash, that don't do that optimisation already, you can use exec to explicitly request that no child process be spawned. Compare:
$ zsh -c '(umask 22 && ps -H); exit'
PID TTY TIME CMD
3806 pts/0 00:00:00 zsh
3868 pts/0 00:00:00 zsh
3869 pts/0 00:00:00 ps
$ bash -c '(umask 22 && ps -H); exit'
PID TTY TIME CMD
3806 pts/0 00:00:00 zsh
3870 pts/0 00:00:00 bash
3871 pts/0 00:00:00 bash
3872 pts/0 00:00:00 ps
$ bash -c '(umask 22 && exec ps -H); exit'
PID TTY TIME CMD
3806 pts/0 00:00:00 zsh
3884 pts/0 00:00:00 bash
3885 pts/0 00:00:00 ps
In bash, contrary to zsh, do not use exec for builtins or functions as that would not run the builtin / functions but try to run an external command by that name instead.
$ bash -c '(printf "%(%F)T\n" -1)'
2022-02-27
$ bash -c '(exec printf "%(%F)T\n" -1)'
printf: %(: invalid conversion specification
The latter called /usr/bin/printf which doesn't support %T (bash extension inspired by ksh93's). Use type cmd to check whether cmd is builtin or not in your particular version of bash.