How to capture a type then cast to it

I have two values, the expectation is that they are both the same type but the 2nd value could be of type Any? and I want to cast that value to the type of the first value. I tried the following but keep getting the error that type is not in scope

let type = type(of: value1)
var value2 = value2 as? type

Is there a way I can cast to a type that I've captured?

The right hand side of a type casting operator has to be a type name, so you can't use a metatype value in that position (this is also why you don't have to write casts as, e.g., as? String.self).

Could you expand on your example a bit? Even if you could cast in this way, it's not clear to me what useful work you could do with value2 once it has been cast to type. If you're just trying to figure out "are these two values of the same type?", you can compare their types directly with ==:

let areSameType = type(of: value1) == type(of: value2)

This should work:

func cast<T, U>(_ t: T, to: U.Type) -> U? {
  return t as? U
}

Usage:

if let x = cast(a, to: type(of: b)) {
  // x is the same type as b
}

This works when the type of b is either concrete or a generic placeholder. If it is an existential you can try _openExistential, although that is not an official part of the language.

• • •

You could also write this function:

func staticType<T>(of: T) -> T.Type {
  return T.self
}

Which enables this to work even when b has existential type:

if let x = cast(a, to: staticType(of: b)) {
  // x has the same static type as b
}
1 Like

I don't think this has quire the right semantics for classes either. Consider:

class C {}
class A: C {}
class B: C {}

let a: Any? = A()
let b: C = B()

if let x = cast(a, to: type(of: b)) {
  print("Same type")
}

print(type(of: a) == type(of: b))

Prints:

Same type
false
1 Like

Wouldn't you want to check

print(type(of: b) == type(of: x))

really? Because of course a and b have different types.
Or am I misunderstanding?

1 Like

Note that at the point where we're doing the == comparison, x is out of scope, so we can't really use it there. But we could add a print(type(of: x) == type(of: b)) into the if let block:

if let x = cast(a, to: type(of: b)) {
    print("Same type")
    print(type(of: x) == type(of: b))
}

The result is the same:

Same type
false

Anyway, since x and a always refer to the same value no matter how we initialize a (since cast(_:to:) passes the value straight through when the cast succeeds), AFAIK type(of: a) and type(of: x) should always be identical.

1 Like

Au contraire:

let a: Int? = 0
let b: Int = 1

if let x = cast(a, to: type(of: b)) {
  print(type(of: x) == type(of: a))   // false
}

(Same result with a and b switched. Plus bridging conversions, eg. NSString and String).

1 Like

Ah, well, fair enough. :slightly_smiling_face:

At this point I usually hand-wave over optional-flattening weirdness.