Can’t convert to KeyPath<Root, any Protocol>

I have a class Case whose properties have types that conform to a protocol CaseIndexProperty. I.e.,

protocol CaseIndexProperty { 
    func displayString() -> String
}

struct CaseID : CaseIndexProperty { ... }
struct DecisionDate: CaseIndexProperty { ... }
struct Locations: CaseIndexProperty { ... }

class Case {
    var id: CaseID
    var decisionDate: DecisionDate
    var locations: Locations
}

I want to be able to pass an array of key paths to these properties to a function. This appears to work (at least, it compiles):

func miniIndex(cases: [Case],
               properties: [KeyPath<Case, any CaseIndexProperty>])
    -> [[String]]
{
    return cases.map { `case` in
        properties.map { property in
            `case`[keyPath: property].displayString()
        }
    }
}

The problem is. I can’t create the array.

let properties: [KeyPath<Case, any CaseIndexProperty>] = [\Case.locations, \Case.decisionDate]

produces this error, even though Locations and DecisionDate conform to CaseIndexProperty:

Key path value type 'Locations' cannot be converted to contextual type 'any CaseIndexProperty'
Key path value type 'DecisionDate' cannot be converted to contextual type 'any CaseIndexProperty'

Do I have fall back to using an array of PartialKeyPaths, and then cast each the values back down to CaseIndexProperty in the consumer?

I'd have to look up the variance rules for key paths, to know if that's intentional or not.

Either way, closures work just like in the days before key paths.

func miniIndex(
  cases: [Case],
  properties: [(Case) -> any CaseIndexProperty]
) -> [[String]] {
  cases.map { `case` in
    properties.map { $0(`case`).displayString() }
  }
}

miniIndex(
  cases: [],
  properties: [
    \.locations as (_) -> _,
    \.decisionDate as Closure
  ]
)

typealias Closure<Input, Output> = (Input) -> Output

Thanks. That makes the project more manageable.

1 Like