I guess you misunderstand. It is fine to put type constraint in function definitions but they should be loose enough for AD to work. In your case I would remove all type constraint in f!. This is what your link is referring too. If you force f! to accept only real-valued inputs for example, it would error Forwarddiff because it wants to use Dual.
The second mistake is that x::Array{Real, 1} is “harmful” because it forces the compiler not to use a concrete type (like Array{Float64, 1}), and as said in the docs Real is an abstract supertype. It is (or I ) mostly used for dispatch by putting type constraint in the arguments of functions.