You gave the compiler too many options, and it chose the wrong one (at least not the one you need). The problem is that every T can be trivially upgraded to T? including T? (upgraded to T?? ).
someNumber = self.mapped(dictionary, key: "someNumber") ?? someNumber
Wow. These types. So, optional .: D
So, as Swift begins to understand this thing. Well, someNumber is Double? so he is trying to turn this into:
Double? = Double?? ?? Double?
It works? Let's look at general mapped , starting with the most specific.
func mapped<T where T:SomeProtocol>(dictionary: NSDictionary?, key: String) -> T? {
To do this job, T must be Double? . Is Double?:SomeProtocol ? Nope. We move on.
func mapped<T>(dictionary: NSDictionary?, key: String) -> T? {
It works? Of course! T maybe Double? We return Double?? and everything is allowed.
So why does this work?
someNumber = self.mapped(dictionary, key: "someNumber") ?? someNumber!
This permits:
Double? = Optional(Double? ?? Double)
And then everything works the way you think they should.
Be careful with so many options. Could someNumber be optional? If any of these things throw ? (I do not suggest throw be a general workaround for additional tasks, but at least this problem gives you the opportunity to think about whether this is really an error condition.)
It is almost always a bad idea to enter parameterization solely on the return value in Swift using the mapped method. This is usually a real mess in Swift (or any generic language that has many types of output, but it really explodes in Swift when there are options associated with it). Type parameters should usually be displayed in arguments. You will see a problem if you try something like:
let x = test.mapped(...)
He will not be able to infer type x . This is not an anti-pattern, and sometimes it is a hassle (and, frankly, the problem you are solving may be one of those cases), but avoid it if you can.
But these are the Options that are killing you.
EDIT: Dominic asks a very good question why it behaves differently when the remote version of mapped removed. I dont know. Obviously, the type matching mechanism checks for valid types in a slightly different order, depending on how many mapped methods are common. You can see this by adding print(T.self) to the mapped<T> . This may be considered a compiler error.