While both of the current answers give suitable workarounds, neither explains what's going on here. The problem is that if we have two types, like this:
trait Foo
trait Bar
And an implicit conversion (or view) from one to the other:
implicit def foo2bar(foo: Foo): Bar = new Bar {}
A conditional with a Foo
for its then
clause and a Bar
for its else
clause will still be typed as the least upper bound of Foo
and Bar
(in this case Object
, in your case Product with Serializable
).
This is because the type inference system isn't going to step in and say, well, we could view that Foo
as a Bar
, so I'll just type the whole thing as a Bar
.
This makes sense if you think about it—for example, if it were willing to handle conditionals like that, what would it do if we had implicit conversions both ways?
In your case, the then
clause is typed as a JObject
, and the else
clause is a (String, String)
. We have a view from the latter to the former, but it won't be used here unless you indicate that you want the whole thing to end up as a JObject
, either by explicitly declaring the type, or by using the expression in a context where it has to be a JObject
.
Given all of that, the simplest workaround may just be to make sure that both clauses are appropriately typed from the beginning, by providing a type annotation like this for fld1
:
val fld1: JObject = "fld1" -> "always"
Now your conditional will be appropriately typed as it is, without the type annotation.