A better description (hopefully): each element of an Array (or other container) has it’s own concrete type, but the type of the container value doesn’t necessarily encode fully the types of the elements. For example:
julia> [nothing, missing]
2-element Vector{Union{Missing, Nothing}}:
nothing
missing
julia> [Val{1}(), Val{2}()]
2-element Vector{Val}:
Val{1}()
Val{2}()
julia> struct A end
julia> struct B end
julia> [A(), B()]
2-element Vector{Any}:
A()
B()
julia> Union{A, B}[A(), B()]
2-element Vector{Union{A, B}}:
A()
B()
In each of these examples the vector elements have a certain concrete type: abstract types can’t have instances. But abstract types can be type parameters to other types, such as container types, like Array.
Quoting Types · The Julia Language
There is no meaningful concept of a “compile-time type”: the only type a value has is its actual type when the program is running. This is called a “run-time type” in object-oriented languages where the combination of static compilation with polymorphism makes this distinction significant.
Only values, not variables, have types – variables are simply names bound to values, although for simplicity we may say “type of a variable” as shorthand for “type of the value to which a variable refers”.