Reputation: 2070
I have a method that parses YAML files. The returned object is a nested Hash, where the keys are always Strings and the leaf-values are always strings, e.g.
{
"a" => "foo",
"b" => {
"c" => "bar",
"d" => "baz"
}
}
I don't know in advance how deep the hash is.
The closest I got to typing the return value was the following signature:
T.any(T::Hash[String,String], T::Hash[String,T::Hash[String, T.untyped]])
This is obviously a bad solution, since it doesn't check anything beneath the second nesting, but the documentation about custom types seems a bit sparse.
Is there any way to type nested hashes, using a custom type, nested types or something similar?
Upvotes: 2
Views: 1402
Reputation: 3606
Unfortunately, you won't be able to do much more than what you got to at this point. Even though Shapes
are supported, they are an experimental feature.
If you really want to go with hashes, you could express it as:
MyType = T.type_alias {T::Hash[String, T.any(String, T::Hash[String, T.untyped])]}
Alternatively, you could use a T::Struct
:
class MyType < T::Struct
const :key, String
const :value, T.any(String, MyType)
end
You'd still have the uncertainty of what the type of the value
is, but with flow sensitivity, it should be easy to process the structure:
class Processor
extend T::Sig
sig {params(my_object: MyType).returns(String)}
def process(my_object)
key = my_object.key
obj_value = my_object.value # this is needed for flow sensitivity below
value = case obj_value
when String
value
when MyType
process(obj_value)
else
T.absurd(obj_value) # this makes sure that if you add a new type to `value`, Sorbet will make you handle it
end
return "key: #{key}, value: #{value}"
end
end
Upvotes: 2