`Cannot find type 'Key' in scope` when extending optional Dictionaries

I'm trying to write this simple extension for generic optional Dictionaries with unspecified Keys and Values, both optional.

The following works fine for non-optional Dictionaries:

extension Dictionary {
    func containsKey(_ key: Key) -> Bool {
        return keys.contains(key)
    }
}

But when I try to adapt this to optional Dictionaries, I get the errors Cannot find type 'Key' in scope and Cannot find 'keys' in scope.

extension [Any?: Any?]? {
    func containsKey(_ key: Key) -> Bool { // Cannot find type 'Key' in scope
        guard let self else { return false }
        return keys.contains(key) // Cannot find 'keys' in scope
    }
}

I'm grateful for any help!

I am surprised that “extension [Any?: Any?]? {}” compiles at all.

The compiler correctly prohibits declaring a variable of that type:

let x: [Any? : Any?]?
// error: 'Dictionary' requires that 'Any' conform to 'Hashable'

It is not clear to me what exactly you are trying to do. What type or types are you trying to extend? What type do you expect “Key” to represent?

Are you trying to create a parameterized extension of Optional where Wrapped is a Dictionary with unspecified generics?

…or where Wrapped is a Dictionary for which both Key and Value are themselves Optional types with unspecified Wrapped values? That seems inadvisable even if it were to become possible.

Thanks for your answer!
I'm trying to extend generic optional Dictionaries with unspecified Keys and Values, both optional.

Swift does not currently support parameterized extensions.

What are you actually trying to achieve? What problem are you trying to solve?

In other words, what situation has led you to want to do what you described?

I'm trying to simplify my code with this extension so that to get a non-optional Bool whether a Dictionary contains a Key, I can write just this:

let dict: Dictionary? = ["Key": "Value"]
dict.containsKey("Key")

instead of this:

let dict: Dictionary? = ["Key": "Value"]
dict?.keys.contains("Key") ?? false

The type of dict in your example is Dictionary?, which is the same as Optional, so the type you need to extend here is Optional (where Wrapped == Dictionary), not Dictionary itself.

2 Likes

Thanks for your reply!
Could you please give me a working example? I have tried this, but there are still several errors:

extension Optional where Wrapped == Dictionary<Any?, Any?> { // 'Dictionary' requires that 'Any' conform to 'Hashable', Type 'Any' does not conform to protocol 'Hashable'
    func containsKey(_ key: Key) -> Bool { // Cannot find type 'Key' in scope
        guard let self else { return false }
        return keys.contains(key) // Cannot find 'keys' in scope
    }
}

Why do you have an optional dictionary in the first place?

What benefit do you think you are getting from that, that you wouldn’t get from a regular non-optional dictionary?

2 Likes

I think you need to put the constraints directly on the containsKey method and then pass it to Dictionary:

extension Optional {
    func containsKey<Key: Hashable, Value>(_ key: Key) -> Bool where Wrapped == Dictionary<Key, Value> {
        guard let self else { return false }
        return self.keys.contains(key)
    }
}
3 Likes

Wow, this just works, thank you so much!

Making too-specific extensions like this is fetid practice, but if you do it anyway, the Hashable constraint is redundant.

extension Optional {
  func contains<Key, Value>(key: Key) -> Bool where Wrapped == [Key: Value] {
    self?.keys.contains(key) == true
  }
}

I prefer == true to ?? false and would just write it as

dict?.keys.contains("Key") == true
2 Likes