KeyPath<T, U?> required, but I only have KeyPath<T, U>

What are my options when a KeyPath<T, U?> is required, but I “only” have KeyPath<T, U>?

struct Foo {
    let bar: String
}

let path: KeyPath<Foo, String?> = \Foo.bar // doesn’t compile

It’s similar with getters (when T -> U? is required, but I only have T -> U), but that can be reasonably solved with a trivial “adapter” that turns U into U?. Is there a similar reasonable shortcut for key paths, other than:

extension Foo {
    var optionalBar: String? {
        return bar
    }
}

let path: KeyPath<Foo, String?> = \Foo.optionalBar

Would it make sense if I could assign KeyPath<T, U> to KeyPath<T, U?> the same way I can now assign Array<T> into Array<T?>?

3 Likes

I don't have a better answer to the question as you already showed in the second code snippet but I wonder if we could extend key-paths with functions like mapValue and compactMapValue which would operate on Value and transform the value to the generic type of the function.

let path = \Foo.bar
let mappedPath = path.mapValue { Optional($0) } // escaping a `Value -> Value?` closure for transformation during extraction of the value 

let value = foo[keyPath: mappedPath] // will extract `String` and then map it into `String?` with the above closure

@Joe_Groff what do you think?

3 Likes

Yes, it would be nice for key paths to play better with other language features, related thread.

Does anybody know if there are any updates on this. It seems there is no easy way to have a function that accepts KeyPath<Foo, String> OR KeyPath<Foo, String?> as a parameter.