The issue you’re seeing is that Hash(String, Array(String) | String) is NOT the same type as Hash(String, Array(String)). You would need to cast your hash to the proper type. For example.
HTTPHeaders.new({"foo" => ["one","two"]} of String => Array(String) | String)
The reasoning is since the ivar should allow both Array(String) and String values, while the hash you’re trying to pass in would not allow plain String values. using of ... tells it that it should also allow String values.
If you could assign a hash of type Hash(String, Array(String)) to a variable restricted to Hash(String, Array(String) | String), what would happen if you call #<<(String) on that variable? The variable is restricted to a hash type with Array(String) | String values, so you should be able to add String. The actual object needs to be able to handle such a call, hence the requirement to for exact type matching, no invariants.
An easy way to transform the hash is hash.transform_values { |value| value.as(Array(String) | String) }
I would probably just use the stdlib’s HTTP::Headers type versus trying to roll your own. Then you could type stuff to HTTP::Headers and use it via HTTP::Headers{"content-type" => "text/css"}.