Well, @Bergi's comment is right of course, I should provide my attempt at a solution.
So far my best version is the following. I'll update when I find something nicer. (EDIT: updated, now with overloads and typing):
function merge<K>(a:K, b:K):K;
function merge<T, K>(a: Map<T, K>, b: Map<T, K>): Map<T, K>;
function merge<T, K>(a: Map<T, K>, b: Map<T, K>):any{
    // overload 1
    if (!(typeof (a) == "object")) {
        return b;
    }
    if (!(typeof (b) == "object")) {
        return a;
    }
    // overload 2
    let merged = new Map();
    a.forEach((a_value, key)=>{
        const b_value = b.get(key);
        if(b_value){
            merged.set(key, merge(a_value, b_value));
        }
        else{
            merged.set(key, a_value);
        }
    })
    b.forEach((b_value, key)=>{
        const a_value = a.get(key);
        if(!a_value){
            merged.set(key, b_value);
        }
    })
    return merged;
}
let a = new Map<string, Map<string, number>>();
a.set('a', new Map(Object.entries({ 'c': 1 })));
a.set('b', new Map(Object.entries({ 'c': 1 })));
let b = new Map<string, Map<string, number>>();
b.set('b', new Map(Object.entries({ 'd': 2 })));
let merged = merge(a,b);
console.log(merged)