@propertyWrapper @dynamicMemberLookup
struct Wrapper<Wrapped> {
var wrappedValue: Wrapped
subscript<Member>(
dynamicMember keyPath: WritableKeyPath<Wrapped, Member>
) -> Wrapper<Member> {
get {
Wrapper(wrappedValue: wrappedValue[keyPath: keyPath])
// error: cannot convert return expression of type 'Wrapper<Wrapped>' to return type 'Wrapper<Member>'
// error: cannot convert value of type 'Member' to expected argument type 'Wrapped'
// fix-it: Insert ' as! Wrapped'
}
set { wrappedValue[keyPath: keyPath] = newValue.wrappedValue }
}
}
I think what I'm trying to do should be possible, because SwiftUI does it. Starting from 38:04 in the talk "Modern Swift API Design" from WWDC 2019, it shows that SwiftUI's Binding's dynamic member lookup returns a new Binding:
@propertyWrapper @dynamicMemberLookup
public struct Binding<Value> {
...
public subscript<Property>(
dynamicMember keyPath: WritableKeyPath<Value, Property>
) -> Binding<Property> {
...
}
}
For generic declarations, using the self type (Wrapper) or its member type (Wrapper.XXX) infers the generic parameter (Wrapped) to match. This has pretty high priority and usually cause the typechecking to fail when the generic parameter is meant to be something else, Wrapped <~ Member in this case.
I'm sure I asked the same question years back, if only I could find it... IIRC, it was due to how this inferred default got applied before anything else in the typechecker.
My personal take is that you need to be watchful anyway if you're in a context with Wrapped, but Wrapped means something else (Member).
Thanks! The example makes it much easier to understand what is going on. So basically, everywhere in Foo<Value> itself, Foo is always interpreted as Self. This seems really like a bug to me.