But even in that example you can see that a Vector or Matrix with 1 dimension of size >1 aren’t the only vector-like things. You have a check on the same line for a Tuple…
Probably, the best bet in this example, as with nearly every case involving type checking, is to use multiple dispatch. Anything which you (as the designer of this interface) deem “close enough to a vector” should behave like a vector, which usually means converting it to a vector and passing it to another method to handle.
That could look something like
build_opt_R(val) = build_opt_R(x, Symbol())
build_opt_R(val, symb::Symbol) = "" # default case, anything unexpected goes here
function build_opt_R(val::Union{String, Symbol}, symb::Symbol)
r = string(Val)
if (r == "global") R = " -Rd"
elseif (r == "global360") R = " -Rg"
elseif (r == "same") R = " -R"
else R = " -R" * r
end
return R
end
# helper function. Pretty sure the else case does the same as arg2str...
function _some_name(val, symb)
R = symb ∈ (:region_llur, :limits_llur, :limits_diag, :region_diag) ?
" -R" * @sprintf("%.15g/%.15g/%.15g/%.15g", val[1], val[3], val[2], val[4]) :
" -R" * @sprintf("%.15g/%.15g/%.15g/%.15g", val[1], val[2], val[3], val[4])
return R
end
# most restrictive/specific case. Only tuples of real numbers of length 4 or 6 are allowed
build_opt_R(val::Union{NTuple{4, Real}, NTuple{6, Real}}, symb::Symbol) = _some_name(val, symb)*"+r"
# for any array, try turning it into a tuple. If it's an acceptable kind,
# it'll go through the method above. Otherwise it'll go to the default case.
# Worth noting this is a type unstable conversion, but this doesn't seem like it
# needs to be performant. If it does, reverse the cases so that Vector
# is the base case, and Tuple and AbstractArray both forward to it with vec!
build_opt_R(val::AbstractArray, symb::Symbol) = build_opt_R(Tuple(val), symb) # can be widened pretty easily to include any iterable if you want.
build_opt_R(val::GDtype, symb::Symbol) = _some_name(val[1].bbox, symb)
build_opt_R(val::GMTdataset, symb::Symbol) = _some_name(val.ds_bbox, symb)
build_opt_R(val::GItype, symb::Symbol) = _some_name(val.range[1:4], Symbol()) # I'm cheating here with symb to copy the behavior in the original
Pretty sure this does the same thing, but I haven’t run it through your test suite. Also, apologies for _some_name
… it’s late 
Of course, I don’t mean to suggest you should always allow all AbstractArray
s of any dimension all over your code, and probably the above is way too permissive. Just an example.
It’s just as common to just force the caller to explicitly convert from a matrix (or whatever) to a vector and not bother with any of this.