Optional type in Swift have problem with chaining

Hi all,
I have code like below:

     extension Optional where Wrapped == Int {
            func toBool() -> Bool {
                guard let sSelf = self else { return false }
                return sSelf > 0
            }
        }
    
        protocol ConfigProtocol {
            var isEnable: Int { get }
        }
        class ConfigA: ConfigProtocol {
            var isEnable: Int = 0
        }

    class Foo {
        func getConfig() -> ConfigProtocol? {
            return ConfigA()
        }
    }

I use this code above:

    let foo = Foo()
    print("Type is:\(type(of: configValue))")

The result of print is Type is:Optional<Int>.
But if I call let isEnableOfInt = foo.getConfig()?.isEnable.toBool() will get the error:
Value of type 'Int' has no member 'toBool'
So confuse: it's Optional not Int ?
But if I change to:

let configValue = foo.getConfig()?.isEnable
print(configValue.toBool()) //false

It worked properly. Is this a bug with Optional?

Hello @nhatlee

You get this error because in the optional chain, isEnable has the type Int, which is not Optional<Int>, and has no toBool method:

// WRONG
let isEnableOfInt = foo.getConfig()?.isEnable.toBool()
//                                      Int ^

You can avoid this error by "escaping" the optional chaining with parenthesis, so that you get the Optional<Int> you want:

// OK
let isEnableOfInt = (foo.getConfig()?.isEnable).toBool()
//                              Optional<Int> ^
2 Likes

Thanks for your help. Your solution worked. But I still feel confused with the result print of it's type: Type is:Optional<Int>. With the type is Optional then the developer should not use parenthesis for this case to get the code work.

Please consider this other optional chain:

// A valid optional chain
let value = foo.bar?.baz.qux.blah.value // An optional value

In this optional chain, there is a single optional property, which is bar. All other properties are non-optional. Yet, thanks to optional chaining, we have to write ? only once, on the only optional property.

If what you ask for were implemented, then all elements in the chain would be optionals. Why not? Because we'd have to write instead:

// A not very nice optional chain
let value = foo.bar?.baz?.qux?.blah?.value // An optional value

The Swift designers have decided that this was not very good. Not only do you have to write many ?, but also you lose information: you don't quite know which properties are optionals, and which properties are not.

I tend to think that you will eventually agree with the language designers.

The consequence is that we have to learn that optional chains preserve the types of all members in the chain, optional or not, with the only exception of the last one, the value which goes out form the chain: this one is always an optional.

And today you have learned that optional chains can be wrapped in parenthesis, so that you can force an optional wherever you want.

So you get:

  • nice syntax
  • ability to finely control your optionals when you need to

This looks like a fine tool to me :-)

3 Likes