For reference, it looks like clojure.tools.emitter.jvm DOES preserve the type of non-primitive fields....
(require '[clojure.tools.emitter.jvm :as e])
(e/eval '(deftype blah [^String x]) {:debug? true})
;;;
// class version 50.0 (50)
// access flags 0x31
public final class user/blah implements clojure/lang/IType {
// compiled from: user/blah
// access flags 0x11
public final Ljava/lang/String; x
...
user> (require '[clojure.reflect :as r])
nil
user> (r/reflect (user.blah. "hello"))
{:bases #{java.lang.Object clojure.lang.IType},
:flags #{:public :final},
:members
#{{:name getBasis,
:return-type clojure.lang.IPersistentVector,
:declaring-class user.blah,
:parameter-types [],
:exception-types [],
:flags #{:public :static}}
{:name user.blah,
:declaring-class user.blah,
:parameter-types [java.lang.Object],
:exception-types [],
:flags #{:public}}
{:name x,
:type java.lang.String, ;;in clojure
:declaring-class user.blah,
:flags #{:public :final}}}}