The problem with which(1) is that it's usually an external program instead of a shell built-in, which means it can't see your aliases or functions and has to try to reconstruct them from the shell's startup/config files.
#!/bin/sh
##
# How to use which on an aliased command
#
# The problem with which(1) is that it's usually an external program instead of
# a shell built-in, which means it can't see your aliases or functions and has
# to try to reconstruct them from the shell's startup/config files.
#
# type is a POSIX-compliant command which is required to behave as if it were a
# built-in (that is, it must use the environment of the shell it's invoked from
# including local aliases and functions), so it usually is a built-in.
#
# type options:
# -a display all locations containing an executable named NAME;
# includes aliases, builtins, and functions, if and only if
# the `-p' option is not also used
# -f suppress shell function lookup
# -P force a PATH search for each NAME, even if it is an alias,
# builtin, or function, and returns the name of the disk file
# that would be executed
# -p returns either the name of the disk file that would be executed,
# or nothing if `type -t NAME' would not return `file'
# -t output a single word which is one of `alias', `keyword',
# `function', `builtin', `file' or `', if NAME is an alias,
# shell reserved word, shell function, shell builtin, disk file,
# or not found, respectively
#
# References:
#
# - https://unix.stackexchange.com/questions/10525/how-to-use-which-on-an-aliased-command
# - https://linuxcommand.org/lc3_man_pages/typeh.html
# - https://jonlabelle.com/snippets/view/shell/type-command
##
$ type -a cat
# cat is /Users/jon/bin/cat
# cat is /bin/cat