You may use package reflect (the fmt package does that too under the hood). You may start from the pointer to the type, and use a typed nil pointer value without allocation, and you can navigate from its reflect.Type descriptor to the descriptor of the base type (or element type) of the pointer using Type.Elem().
Example:
t := reflect.TypeOf((*ID)(nil)).Elem()
name := t.Name()
fmt.Println(name)
Output (try it on the Go Playground):
ID
Note: be aware that Type.Name() may return an empty string (if the Type represents an unnamed type). If you're using a type declaration (with the type keyword), then you already named the type, so Type.Name() will return a non-empty type name. But using the above code for a variable of type *[]string for example will give you an empty string:
var s *[]string
t := reflect.TypeOf(s).Elem()
name := t.Name()
fmt.Printf("%q", name)
Output (try it on the Go Playground):
""
See related questions:
Golang reflect: Get Type representation from name?
Identify non builtin-types using reflect