See this comment in particular, but some of the surrounding discussion as well.
The short answer is that broadcasting getproperty/getfield often doesn’t constant-propagate the field like you want. You get the desired result for x because there’s only one property that could be accessed, but for y it can’t tell whether you’re accessing the a or b property (which each have a different type, hence the uncertain type info).
You’ll get the proper result if you make a “getter” function dedicated to that particular field and broadcast or map that. I recommend you try something like map(z -> z.a, y) or (z -> z.a).(y), or map/broadcast a dedicated accessor function that you make if it’s worth making such a function (both of my recommendations do this, but only using a throw-away anonymous function).
julia> f(y) = getproperty.(y, :a)
f (generic function with 1 method)
julia> f(y)
Any[]
julia> f(y) = map(s->s.a, y)
f (generic function with 1 method)
julia> f(y)
String[]
julia> f(y) = map(s->getproperty(s, :a), y)
f (generic function with 1 method)
julia> f(y)
String[]