This is actually a pretty good illustration why typedef'ing pointer types in C can lead to unexpected behaviour.
Let's say you have
volatile char *example(void);
Because the volatile is on the left side of the asterisk, it applies to the pointed to value. Writing
char *volatile example(void);
means the pointer itself is volatile, not the pointed to value.
Now, let's say you have
typedef char * mytype;
volatile mytype example(void);
Do you know what the volatile refers to this time?
Yes, to the pointer, and not to the pointed-to value. That is, it is equivalent to
char *volatile example(void);
and not to
volatile char *example(void);
If you accept that typedef is not just shorthand, that it isn't equivalent to a preprocessor macro definition (#define mytype char * would behave the exact opposite way), and that it actually, really defines a new type, it makes perfect sense.
It also means that defining an opaque cookie type, say
typedef void * handle;
makes perfect sense: it is an unknown thing you obtain from somewhere and pass along as needed, but can never examine in detail, only as far as its address (and even that in limited ways, because of C's pointer rules). Even though it is a pointer, it points to void, so you cannot dereference it as-is, either. Doing stuff like
const handle h = ...;
in a function makes perfect sense too. It means the cookie, the variable h is const, that us programmers promise to not try and change its value in the current scope. It says nothing about what it might point to.