This is because of how
Codable is composed here between
Optional<T>.init(from:) attempts to decode a
T if the contents of the current container are not
nil. It has no knowledge of what
T is specifically, only that it is
Decodable — in this case, it performs no introspection of
T to try to figure out whether the value is valid or not.
In this case,
Number.init(from:) is not a failable initializer, nor could it be. In
Number.init(from:), there is no way to know the outer context of how things are being decoded — if
number was declared as just
let number: Number, returning
nil would neither be valid, nor possible. So
Number.init(from:) can't return
nil, it can only
throw when something goes wrong.
Optional not knowing what a
Number should look like, and
Number not being able to know or make use of the fact that it is inside of an
Optional, there's nothing to do here but throw.
Optional could attempt to catch certain errors to return
nil instead of propagating, but that could easily result in a loss of information.
In general, it's better to err on the side of being conservative in terms of throwing away error information. If
Optional did catch errors to perform this, but you actually wanted the opposite behavior, there would be no way to retrieve that error information back. With this scheme, it's at least possible to interject around
decodeIfPresent(Number.self, forKey: .number) to catch errors or introspect in order to treat the result as
nil, as @gwendal.roue offers above.