I believe what you're running into here is the difference between TypeA.Type
and TypeA.Protocol
. These are subtly different and unfortunately once generics are involved it is possible to write in code a type reference that looks like it should resolve to TypeA.Type
when really it resolves to TypeA.Protocol
.
The difference is a little bit hard to describe, but in short:
-
TypeA.Type
refers to what is called the "existential metatype," which is the supertype of all types which conform toTypeA
. -
TypeA.Protocol
refers to the "protocol metatype", which is the type ofTypeA.self
.
The (natural) language we have to describe all of this is... not great and I have talked myself in circles about it before. You can read this thread which contains what might be the most thorough exploration of .Type
vs. .Protocol
to date, though I'm not sure you'll come out of that thread less confused than going in.
It might be easiest to think about this in terms of examples. Because TypeA.Type
is the supertype of all conforming types, there are things you can do with a value of type TypeA.Type
that you can't do with a value of type TypeA.Protocol
. For example, if TypeA
has a static requirement, we can call that requirement on a value of type TypeA.Type
:
protocol TypeA {
static func test() -> Int
}
class TypeB: TypeA {
static func test() -> Int { 0 }
}
func f (_ t: TypeA.Type) {
print(t.test()) // 0
}
Because the underlying value of t
must be some type which conforms to TypeA
, we know that there must be an implementation of test()
available for us to invoke. OTOH, the value TypeA.self
has no such implementation because TypeA
only specifies the requirement that conforming types must implement. So it is not possible to call test()
on a value of type TypeA.Protocol
.
The last piece of the puzzle here which I mentioned briefly above, is that in generic contexts, T.Type
refers to T.Protocol
when T
is bound to an existential type, because we want it to be the case that T.self
is always of type T.Type
. (Notably, it is not the case that TypeA.self is TypeA.Type
, but we sacrifice this in concrete contexts on the assumption that when people write TypeA.Type
what they usually want is "some meta type which conforms to TypeA
."
This extra wrinkle can result in some pretty confusing behavior, and I'm not actually certain whether the behavior you've identified here should be classified as a bug or not. We have all the concrete information needed to know that Optional<TypeA>.WrappedType
is TypeA
, so it's a little strange that the usual rule for generic meta type resolution would kick in. Maybe there's a reason for this, though.