@Joseph-Sible-Reinstate-Monica pi me on the right path here, although I had to do a bit of extra work because I'm using Zig.
Zig imports C code by translating it to Zig, and unfortunately one of the areas where this can fail is when macros are heavily involved, as is the case here. However, by looking at what the macros actually do I was able to find the functions which return a widget identifier for each given type of widget, and then just match that up with the identifier which is embedded inside the widget data structure. And it's way in there...
ptr.*.parent_instance.g_type_instance.g_class.*.g_type;
In Zig, .* means dereference the pointer, so we deref the widget pointer, get the patent_instance field, then the g_type_instance, g_class, deref that, and finally arrive at the destination. Fun. I'm glad it at least works out to a one-liner. Then match that up with (for a GtkWindow) gtk_window_get_type(). Of course it's slightly more complicated, because in that case I'd also have to check against GtkApplicationWindow and others, but at any rate I found a workable solution.
Thank you all for answering.