UPDATE: 2019-05-30 with the release of TypeScript 3.5 this should be addressed by smarter union type checking. The following applies to 3.4 and below:
This issue has been reported before and it's essentially a compiler limitation. While you and I understand that {a: X} | {a: Y} is equivalent to {a: X | Y}, the compiler does not.
In general you can't combine unions of objects into objects of unions unless the objects differ only in the type of one property (or rather that the differing properties represent all possible distributions of the unions). For example, you can't collapse {a: X, b: Z} | {a: Y, b: W} to something like {a: X | Y, b: Z | W}... the type {a: X, b: W} is assignable to the latter but not the former.
Imagine writing the compiler to try to make such reductions; for the vast majority of cases it would spend precious processor time examining union types only to find that it cannot combine them. The relatively rare cases like yours where the reduction is possible are probably just not worth it.
Even in your case, I'm skeptical; are you really trying to make a discriminated union where only the discriminant property is different? That's a pathological case. The intended use case for discriminated unions is to take a set of different types and add a single property to let you know which type a value is. As soon as you start adding other properties which make the discriminated types truly different, you would have to abandon the idea of collapsing the union.
So, assuming you really need the non-discriminated discriminated union above, what can you do? The easiest way is to just tell the compiler you know what you're doing by using a type assertion:
function test(input: C): AOrB {
return input as AOrB; // you know best
}
This now compiles, although it's not really safe:
function test(input: C): AOrB {
return (Math.random() < 0.5 ? input : "whoopsie") as AOrB; // also compiles
}
A safer yet more complicated solution is to walk the compiler through the different possibilities:
function test(input: C): AOrB {
return input.kind === 'a' ? {kind: input.kind} : {kind: input.kind};
}
Either of those or something else you come up with should work.
Okay, hope that helps; good luck!