Usually structs with many fields don’t have such many types, that is, many fields share the same type, such that something like struct A{T1,T2,T3} ... end
tends to suffice.
Now, if you really want all possible flexibility without annotating anything, but still keeping fields concrete, you man just want to define a named tuple:
julia> test1(x::String) = (; :x => _TestStringReference(x))
test1 (generic function with 1 method)
julia> test1("foobar")
(x = _TestStringReference("foobar"),)
julia> @btime test1("foobar")
2.186 ns (0 allocations: 0 bytes)
(x = _TestStringReference("foobar"),)
and, eventually wrap the named tuple in a struct for dispatch, such as
julia> struct _MyTest1{T<:NamedTuple}
x::T
end
and define getproperty
operations for the _MyTest1
type.