25

I’m trying to determine if a given type t (Any.Type) is an optional type, I’m using this test

t is Optional<Any>.Type

but it returns always false.

So is there any way to achieve this?

idmean
  • 14,540
  • 9
  • 54
  • 83

6 Answers6

29

Assuming that what you are trying to do is something like this:

let anyType: Any.Type = Optional<String>.self
anyType is Optional<Any>.Type // false

Sadly swift currently (as of Swift 2) does not support covariance nor contravariance and type checks directly against Optional.Type cannot be done:

// Argument for generic parameter 'Wrapped' could not be inferred
anyType is Optional.Type // Causes error

An alternative is to make Optional extend an specific protocol, and check for that type:

protocol OptionalProtocol {}

extension Optional : OptionalProtocol {}

let anyType: Any.Type = Optional<String>.self
anyType is OptionalProtocol.Type // true
Maic López Sáenz
  • 10,385
  • 4
  • 44
  • 57
  • 2
    The `OptionalProtocol` can have some nice uses: It can be used to provide [a *generic-less* interface to access the Optional values](http://stackoverflow.com/questions/27989094/how-to-unwrap-an-optional-value-from-any-type/32780793#32780793) and [even the wrapped type](http://stackoverflow.com/questions/32645612/check-if-variable-is-an-optional-and-what-type-it-wraps/32781143#32781143). – Maic López Sáenz Sep 25 '15 at 20:59
  • Very clever! It works perfectly in Swift 5.2 `let x = ; if type(of: x) is OptionalProtocol.Type { ... } ` – David H Feb 24 '20 at 21:43
  • Nice answer. Do you know how to get non-optional type from optional? – fnc12 Sep 14 '21 at 08:27
  • Yep, [this answer shows how to get the wrapped type](https://stackoverflow.com/questions/32645612/check-if-variable-is-an-optional-and-what-type-it-wraps/32781143#32781143) – Maic López Sáenz Sep 30 '21 at 22:37
13

A bit late for the party. But, I ran into the same problem. Here is my code.

func isOptional(_ instance: Any) -> Bool {
    let mirror = Mirror(reflecting: instance)
    let style = mirror.displayStyle
    return style == .optional
}

let a: Int = 1 // false
let b: Int? = 2 // true
let c: Double = 3.0 // false
let d: Double? = 4.0 // true
let e: NSString = "Hello" // false
let f: NSString? = "Hello" // true


isOptional(a) // fasle
isOptional(b) // true - warning
isOptional(c) // false
isOptional(d) // true - warning
isOptional(e) // false
isOptional(f) // true - warning

It looks good to me. swift4

Kaz Yoshikawa
  • 1,577
  • 1
  • 18
  • 26
3

Optionals conform to ExpressibleByNilLiteral, so you may use that:

let t = Optional<String>.self
t is ExpressibleByNilLiteral.Type // true
Andreas Ley
  • 9,109
  • 1
  • 47
  • 57
  • It's important to note that Apple says "Only the Optional type conforms to ExpressibleByNilLiteral. ExpressibleByNilLiteral conformance for types that use nil for other purposes is discouraged.". Otherwise, this would only be a necessary but not a sufficient condition. – idmean Jan 28 '22 at 07:14
2

You can do something like this:

extension Mirror {
    static func isOptional(any: Any) -> Bool {
        guard let style = Mirror(reflecting: any).displayStyle,
            style == .optional else { return false }
        return true
    }
}

Usage:

XCTAssertTrue(Mirror.isOptional(any: Optional(1)))

Or if you need to cast from Any to Optional

protocol _Optional {
    var isNil: Bool { get }
}

extension Optional: _Optional {
    var isNil: Bool { return self == nil }
}

func isNil (_ input: Any) -> Bool {
    return (input as? _Optional)?.isNil ?? false
}

Usage:

isNil(nil as String?) // true
isNil("") // false
ScottyBlades
  • 12,189
  • 5
  • 77
  • 85
0

You can do:

public func isOptionalType(_ type: Any.Type) -> Bool {
  type is ExpressibleByNilLiteral.Type
  // or
  // String(describing: type).hasPrefix("Optional<")
}
(lldb) po isOptionalType(Int.self)
false

(lldb) po isOptionalType(Int?.self)
true
Honghao Z
  • 1,419
  • 22
  • 29
-4

You could use generics to achieve this:

func isOptional<T>(x:T?)->Bool
{
    return true
}

func isOptional<T>(x:T)->Bool
{
    return false
}

Edit

The code above can be used to know if a variable is of optional type. The only way i've figured out to know about a variable containing a type is by using reflection:

var t1:Any.Type=(String?).self
var t2:Any.Type=(String).self
Mirror(reflecting: t1).description
Mirror(reflecting: t2).description

The first call to Mirror gives the string "Mirror for Optional<String>.Type", and the second gives "Mirror for String.Type".

I understand that comparing string is not a convenient way to do this check, i will try again to find something more performant..

hariseldon78
  • 1,027
  • 14
  • 22
  • `t` is never an optional, it’s always a type object (probably representing an optional) so always the second method would be called. – idmean Sep 15 '15 at 17:00