Great! I also agree with everything that QuinceyMorris wrote.
So, if all of us could just hold on for a moment now and think really carefully about what we expect from our "best (possible) implementation".
Can we please make really sure that it will not boil down to essentially the following (don't be upset, just read and think this through a couple of times, as we might easily think that our expectations will not boil down to this, even though they might actually do just that):
#1. We write our code, typechecking and compilation happens here.
#2. We get our executable.
#3. We change something in eg a JSON archive that the executable has been setup to unarchive from.
#4. We run our executable.
#5. We get frustrated about the fact that what happened in #3 can't be known by the compiler at #1.
Thus, If we are _really_ sure that what we are expecting from the best possible solution is in fact not the same as expecting the compiler to know stuff that is only knowable at runtime, then:
The solution to our problem will always be one that essentially does one of these three things:
1. Wraps everything that we like to be statically typed inside some dynamically typed (thus type erasing) construct.
2. Doesn't complicate things without any good reason and just uses dynamically typed constructs for tasks such as this (like eg Objective-C and javascript does everything (including tasks that would be better solved statically in eg Swift)).
3. Makes sure that the (deserializing) code has static type constructs for everything that any possible archive may possibly represent.
Alternative 3 is of course only practical for very limited/simple/specialized/inflexible forms of archives. Stuff that would probably not qualify to be called serializing/archiving. A simple example would be eg: You (and most importantly the compiler!) can know that the archive always describes a UInt32, thus your deserializing code can simply contain something like this:
let unarchivedUInt32 = UInt32(valueFromArchive: archive)
Very simple, but also not very flexible.
IMHO I think solutions that does number 1 might often just be strange and overcomplicated ways of doing number 2. But sometimes it might perhaps be motivated to do number 1.
Perhaps it's easy to miss that no matter if you do number 1 or 2 you will be type erasing, ie you will not get the "good parts" of static typing, typechecking etc "back" from any archive, simply because, again, that would be like expecting to get compile time errors for mistakes in your JSON archive.