There are two answers to your questions here:
There are corner cases here around integer coding keys which don't always have good answers. The biggest issue to deal with is conversion between integer sizes:
- It's always safe to promote an integer type smaller than
Intto anInt, so it would be possible to encode those values asCodingKeys. On decode, though, you could get aCodingKeywhose integer value does not fit in, say, anInt8. You could consider this to be a type mismatch, potentially, but it's one thing for an integer value to not map to a realCodingKey, and another for the integer value to be completely out of bounds. Given the API contract ofCodingKeys, it's reasonable to assume that they should be able to use the entire range ofIntvalues; it's a different matter to expect to decode anInt8value from an unkeyed container and get back something that would require anInt64or similar - A bigger problem is what happens for types like
UInt— if a dictionary hasUIntkeys which all happen to fit in anInt, should it be encoded as a keyed container? And if one of the keys does not fit in anInt? There is an inconsistency in formats here based on the actual values of the keys, and not the types themselves
It is considerably simpler to define the behavior here around the CodingKeys contract itself: all keys have a String value, and potentially an Int value. Those types map losslessly into CodingKeys; otherwise, we do the safe thing of not making assumptions.
The casts would work, certainly, but the question is one of semantics. Just because a type conforms to LosslessStringConvertible, does it mean that it's safe/reasonable to convert it into a string key and back?
- There are types for which the string representation might be significantly larger or more difficult to represent than the underlying
Codablerepresentation - Considering also the interaction with the above types:
Int8isLosslessStringConvertible, which means that in a format that differentiates betweenIntandStringkeys, encoding[1: "one", 2: "two"]as[Int: String]would encode as[1: "one", 2: "two"], while encoding the same value as[Int8: String]would encode it as["1": "one", "2": "two"]; the differences here might be non-obvious if you don't know the original types
From a semantic standpoint, though — it's not necessarily clear what the intent is behind annotating a type with LosslessStringConvertible; just because a type is indeed convertible doesn't necessarily mean we should prioritize that format over their own Codable format.
None of these are insurmountable differences, by the way; it's possible to come up with semantics that make sense for all of these cases. It is, however, significantly simpler to express that "String- and Int-keyed dictionaries use CodingKeys; everything else uses the unkeyed format".