Part of the problem is that what you want doesn’t seem uniquely defined.
For example, if you have x = Any[1.0f0, 2.0], do you want AbstractFloat[1.0f0, 2.0] (the result of map(identity, x) or [x for x in x], corresponding to typejoin), or do you want Float64[1.0, 2.0] (which is a lossless conversion, corresponding to calling promote_type on the types via mapreduce(typeof, promote_type, x), and also what you would get from [1.0f0, 2.0]). Or do you want Union{Float32,Float64}[1.0f0, 2.0], which is narrower than AbstractFloat?
What are you trying to accomplish? If it’s to improve performance, then you probably want a concrete type like Float64 or at worst a union of two or three types — whereas a typejoin like AbstractFloat[...] may be as bad as Any[...] for performance.
That’s literally what promote does (via promote_type(Int, Float64) === Float64).
It’s the difference between promotion (promote and promote_type) versus typejoin (and promote_typejoin).
You can do mapreduce(typeof, typejoin, x) versus mapreduce(typeof, promote_type, x), depending on which behavior you want, if you want to be explicit.
It would be nice to explicitly document that collect on an iterator uses typejoin (this doesn’t seem to be in the current collect docstring?), whereas array literals use promote_type. (map and comprehensions are already documented to behave like collect on iterators.)