mredig
(Michael Redig)
1
So I've written this code as a bit of a convenience:
extension Dictionary where Key == String, Value == Any {
subscript<T>(_ key: String) -> T? {
return self[key] as? T
}
}
But when I use it
// configuration is Any
guard
let configurationDict = configuration as? [String: Any]
else { throw ConfigurationError.incorrectlyFormattedConfigurationDictionary }
let foo: String = configurationDict["bar"]
I get the error Cannot convert value of type 'Any?' to specified type 'String'
Am I crazy, or shouldn't that work?
You need to write:
let foo: String? = configurationDict["bar"]
This is because your convenience subscript returns an Optional<T>, but you're assigning it to a value of type String, which is non-optional.
tera
3
I would NOT redefine the standard subscript operator... better use your own:
extension Dictionary where Key == String, Value == Any {
subscript<T>(myValueForKey key: String) -> T? {
get { self[key] as? T }
set {
self[key] = newValue
}
}
}
Usage:
let foo: String? = configurationDict[myValueForKey: "bar"]
Also note that it is "String?", not "String".
Alternatively you may define a bunch of:
configurationDict[stringForKey: "bar"]
configurationDict[intForKey: "bar"]
...
Note, the benefit at the use site is minimal, compare with normal:
let first = configurationDict[myValueForKey: "bar"] as! String?
let second = configurationDict[myValueForKey: "bar"] as? String
BTW - there's a difference between first and second here, sometimes you'd want the first (if you are sure that the value must be either absent or of a particular type), and sometimes you'd want the second (when the value could be of a different type and that's not an error situation).
1 Like
Or you could supply the type as an argument to the subscript.
extension [String: Any] {
public subscript<T>(key: String, as type: T.Type) -> T? {
self[key] as? type
}
}
And then you can use it like this.
let foo = configurationDict["bar", as: String.self]
1 Like
mredig
(Michael Redig)
5
I tried this and it also gets the same error.
Edit:
I just realized that, in making an example, this is definitely different than I was doing in my actual code. I was actually running
if let foo: String = configurationDict["bar"] {}
which created the residual, non optional single statement when I was trying to simplify for the forums. However, I still did try both this and my original example and they both provide the same error. I think as @tera is suggesting, I can't replace the standard subscript.
mredig
(Michael Redig)
6
That's totally fair! I thought it be an alternative to the standard, not redefine it. (aka, I thought it would use standard when no type is provided, but use the generic when explicitly defined)
My current solution was basically your alternative: stringForKey etc, but I'll see if the generic will work with my own label.
the “proper” way to define this kind of API would be with a metatype parameter like:
extension [String: Any]
{
subscript<T>(key:String, as _:T.Type = T.self) -> T?
{
get { self[key] as? T }
set(value)
{
self[key] = value
}
}
}
3 Likes