> And I don't know how to express the correct solution (i.e. where we actually assert that A and B are object types).
You can do this:
function merge<
A extends Record<string, unknown>,
B extends Record<string, unknown>
>(a: A, b: B): A & B {
return { ...a, ...b }
}
const result = merge({ a: 1 }, { b: 2 })