Typealias resolving can't figure this out

I posted this issue on the Feedback Assistant at Apple... but this really seems to be on Swift's part.

// MARK: - Models
​
public struct Donut {
    public var flavor: String
    public var isGlutenFree: Bool
    public var isVegan: Bool
}
​
// MARK: - Endpoint Protocol
​
public protocol Endpoint {
    associatedtype Result
}
​
extension Endpoint {
    public typealias donuts = MyEndpoints.GetDonuts
}
​
// MARK: - Endpoints
​
public enum MyEndpoints {
    public struct GetDonuts: Endpoint {
        public typealias Result = [Donut]
​
        var glutenFree: Bool = false
        var vegan: Bool = false
​
        public init(glutenFree: Bool = false, vegan: Bool = false) {
            self.glutenFree = glutenFree
            self.vegan = vegan
        }
    }
}
​
// MARK: - Client Protocol
​
public protocol ClientProtocol {
    func get<T: Endpoint>(_ endpoint: T) async throws -> T.Result
}
​
// MARK: - Example
​
func someExample(_ client: ClientProtocol) async throws {
    // Using non-typealias syntax works fine
    let result0 = try await client.get(MyEndpoints.GetDonuts(glutenFree: true))
    // result0 is of type Array<Donut> as expected
​
    // Using typelias `donuts` in protocol Endpoint extension
    let result1 = try await client.get(.donuts())
    // doesn't work compile
}

Why would I expose my endpoints like this ?
Because it's a nice syntax,

  • autocompletion of optional values is lovely with Xcode 14 beta
  • autocompletion of available typealiases exists as well ...

Nothing prevents me to write the typealias and the type alias is correct.

It doesn't work because .donuts does not return an instance of Endpoint. You have two options that I know of:

  1. Write Endpoint.donuts.
  2. Write the boilerplate to forward the GetDonuts initializer, which is really what you want, not a typealias. There was a big thread about making this unnecessary, but it didn't go anywhere.

But donuts should be equal to MyEndpoints.GetDonuts in my opinion, its a type alias it's like replacing it in code. And MyEndpoints.GetDonuts is a struct that responds to protocol Endpoint

No, if you actually wanted a type alias, you would capitalize it. Instead, you're trying to forward an initializer along so that it can be used with static member lookup:

extension Endpoint where Self == MyEndpoints.GetDonuts {
  static func donuts(glutenFree: Bool = false, vegan: Bool = false) -> Self {
    .init(glutenFree: glutenFree, vegan: vegan)
  }
}

I agree that it should be easier to write that sort of code, and I suspect that as more people come across the pain of the boilerplate, we'll at least get better tooling support for generating it.

Found it.