As KeyPath
can't be casted to super types in Swift, I want to write a type-erased version, which represents any KeyPath
whose value could be casted to a specific protocol or super type:
public struct PropertyGetter<Root, ValueType> {
private let keyPath: PartialKeyPath<Root>
public init<T: ValueType>(_ keyPath: KeyPath<Root, T>) {
self.keyPath = keyPath
}
public func get(_ instance: Root) -> ValueType {
return instance[keyPath: self.keyPath] as! ValueType
}
}
The compiler rightfully complains that
type 'T' constrained to non-protocol, non-class type 'ValueType.Type'
as ValueType
could potentially be a struct type.
So how do we properly constrain this? That is enforcing that
ValueType
is either a class type or a protocol, i.e subclassable / implementableT
is constrained to conform toValueType
, i.ex as! ValueType
must be guranteed to work, wherex: T
Notice that it is indeed possible to write such a type-erasing struct, when the Protocol type is fixed. E.g a class which only accepts KeyPath
s pointing to CustomStringConvertible
members:
public struct CustomStringConvertibleGetter<Root> {
private let keyPath: PartialKeyPath<Root>
public init<T: CustomStringConvertible>(_ keyPath: KeyPath<Root, T>) {
self.keyPath = keyPath
}
public func get(_ instance: Root) -> CustomStringConvertible {
return instance[keyPath: self.keyPath] as! CustomStringConvertible
}
}
let getter1 = CustomStringConvertibleGetter(\SomeClass.someString) // works
let getter2 = CustomStringConvertibleGetter(\SomeClass.nonConformingMember) // will throw an error at compile time