How can you check if a type is Optional in Swift?

守給你的承諾、 提交于 2019-12-08 19:53:19

问题


How can you check if a type is Optional in Swift?

Say I have a variable of type PartialKeyPath where:

struct Foo {
    let bar: String
    let baz: String?
}

typealias Property<Root> = (key: PartialKeyPath<Root>, value: Any?)
typealias Properties<Root> = [Property<Root>]

Now say I iterate thru an instance of Properties:

properties.forEach { prop in
    let valueType1 = type(of: prop.key).valueType
    let valueType2 = type(of: value)

    ...

How can I check here whether valueType1 is Optional<valueType2>, or whether it is Optional of any other flavor for that matter?

So far the only way I’ve found is really ugly...


回答1:


This is a hacky but working solution:

func isOptional(_ type: Any.Type) -> Bool {
    let typeName = String(describing: type)
    return typeName.hasPrefix("Optional<")
}

Test:

let t1 = Int?.self
let t2 = Bool.self

print(isOptional(t1))
// true

print(isOptional(t2))
// false



回答2:


Using a similar approach to Optional field type doesn't conform protocol in Swift 3, you could define a 'dummy protocol' for Optional and use this to get the wrapped metatype:

protocol OptionalProtocol {
  // the metatype value for the wrapped type.
  static var wrappedType: Any.Type { get }
}

extension Optional : OptionalProtocol {
  static var wrappedType: Any.Type { return Wrapped.self }
}

If you just want to know a type is an optional:

func isOptionalType(_ type: Any.Type) -> Bool {
  return type is OptionalProtocol.Type
}

print(isOptionalType(String.self)) // false
print(isOptionalType(String?.self)) // true

If you want to check if one metatype is the 'optional version' of another metatype:

struct Foo {
  let bar: String
  let baz: String?
}

struct Property<Root> {
  var key: PartialKeyPath<Root>
  var value: Any
}

let properties = [Property(key: \Foo.baz, value: "hello")]

/// Attempt to get the `Wrapped` metatype from a metatype of an
/// `Optional<Wrapped>`. If not an `Optional`, will return `nil`.
func wrappedTypeFromOptionalType(_ type: Any.Type) -> Any.Type? {
  return (type as? OptionalProtocol.Type)?.wrappedType
}

for property in properties {
  let valueType1 = type(of: property.key).valueType
  let valueType2 = type(of: property.value)

  if wrappedTypeFromOptionalType(valueType1) == valueType2 {
    print("\(valueType1) == Optional<\(valueType2)>")
  }
}

// Optional<String> == Optional<String>

However there's almost certainly a better way to do whatever you're trying to do here with the key paths.




回答3:


could you use a mirror reflecting Any and check displayStyle is optional?.

func isOptional(any:Any) -> Bool {

  let mirror = Mirror(reflecting: any)
  if mirror.displayStyle == .Optional {
      return true
  } else {
      return false
  }

}

More on mirror display style: https://developer.apple.com/documentation/swift/mirror.displaystyle




回答4:


A tweak of @kelin’s answer:

postfix operator ...?!
postfix func ...?!<T>(_ instance: T) -> Bool {
    let subject = "\(Mirror(reflecting: instance).subjectType)"
    return !subject.hasPrefix("Optional") 
}

And in the vein of @Ercell0’s answer is this superior method:

func isOptional<T>(_ instance: T) -> Bool {
    guard let displayStyle = Mirror(reflecting: instance).displayStyle 
        else { return false }
    return displayStyle == .optional
}


来源:https://stackoverflow.com/questions/47690210/how-can-you-check-if-a-type-is-optional-in-swift

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!