Problem Description
I'm encountering a type inference problem when using Swift's ResultBuilder pattern. In the code shown below, the compiler reports an error at the closure call to the build
method: "Type of expression is ambiguous without a type annotation".
protocol PA {
}
protocol B: PA {
}
class C: B {
}
class D: B {
}
class E {
func build(
@Builder builder: () -> [any PA]
) {
}
}
class F {
func getD() -> D? {
return D()
}
func test() {
var e = E()
e.build { //Type of expression is ambiguous without a type annotation
C()
if true {
getD()!
}
}
}
}
@resultBuilder
final class Builder: BaseBuilder<any PA> {
}
class BaseBuilder<E> {
typealias Expression = E
typealias Component = [E]
// MARK: - Expression
static func buildExpression(_ expression: E) -> E {
return expression
}
// MARK: - Block overload
static func buildBlock(_ expressions: E...) -> Component {
return Array(expressions)
}
static func buildBlock(_ expressions: E?...) -> Component {
return expressions.compactMap { $0 }
}
static func buildBlock(_ expressions: [Component]) -> Component {
return expressions.flatMap { $0 }
}
// MARK: - Optional overload
static func buildOptional(_ expression: E?) -> Component {
return if let expression { [expression] } else { [] }
}
static func buildOptional(_ expressions: E?...) -> Component {
return expressions.compactMap { $0 }
}
// MARK: - If-else overload
static func buildEither(first child: Component) -> Component {
return child
}
static func buildEither(second child: Component) -> Component {
return child
}
// MARK: - Array overload
static func buildArray(_ components: [E]) -> Component {
return components
}
}
Solution I've Tried
I've tried adding an explicit type annotation, as shown below:
e.build { () -> [any PA] in
C()
if true {
getD()!
}
}
However, this approach still doesn't resolve the issue.
Analysis of the Problem
Looking at the code structure, I have the following type relationships:
- Protocol
PA
- Protocol
B
inherits fromPA
- Classes
C
andD
both implement protocolB
In the build
method, I'm trying to return both C()
and getD()!
(of type D
) within the closure. Both should conform to the any PA
type requirement, but the compiler cannot correctly infer the type.
My Questions
- Why is the compiler still unable to resolve the type of this closure even with an explicit type annotation?
- In the ResultBuilder pattern, how should I correctly handle returning different types that implement the same protocol?
- Are there other ways to refactor this code to avoid this type inference issue?
Thank you for your help!