I was recently trying to figure out how to print the static type of a value. My initial reaction was just to print the result of passing the value to type(of:). This does not return the intended value though because as stated in its documentation, type(of:) "returns the dynamic type of a value." After playing around with things for a bit longer, I stumbled upon a solution, but I don't totally understand why it works:
enum Foo: Error {
case bar
}
let error = Foo.bar as Error
func staticType<T>(of value: T) -> String {
return "\(type(of: value))"
}
print(type(of: error))
// Prints "Foo"
print(staticType(of: error))
// Prints "Error"
I found this solution I stumbled upon sort of confusing and as such I have a few questions.
Are there any other ways to print the static type of a value? Why does staticType work as it does and not print the dynamic type of error even though it uses type(of:) at its core? Why does wrapping the call to type(of:) in a function produce a different result? What is the difference between the two print calls?
because type(of: value) in a generic context would still return the dynamic subclass of an object instance you pass in.
There are really two variants of type(of:). When you apply it to something known to be a protocol type, it opens the protocol type and gives you the type of the value inside as a protocol metatype. For cases where we don't see that a type is a protocol type, we can only provide the concrete metatype for the value. Inside the context of staticType, even if T == Error, the body of staticType can't see that, so it picks the latter meaning of type(of:).
class Super {}
class Child: Super {}
func staticType1<T>(of value: T) -> String {
"\(type(of: value))"
}
func staticType2<T>(of _: T) -> String {
"\(T.self)"
}
// x's static type is Super
// x's dynamic type is Child
let x: Super = Child()
print(staticType1(of: x)) // Child
print(staticType2(of: x)) // Super
I used both versions to show the difference in implementations.
This is backwards. You're declaring x to have static type Super and underneath at runtime it's really a Child.
I'm using classes here because Joe mentioned that using type(of:) will return the dynamic type for class instances, which in this case is Child, not the expected Super. He also mentioned that even though this is incorrect for classes, it appears to work for protocol types because staticType has no idea that the passed value is a protocol type, so it doesn't look for the dynamic type.