import copy


def overlay(under, over):
    """Recursively copy over onto under.

    If a key in over has the value None, then that key will be deleted
    from the result.

    If a key is in both over and under, both values must have the same
    type.
    """

    result = copy.deepcopy(under)
    for key, value in over.items():
        if value is None:
            try:
                del result[key]
            except KeyError:
                pass
            continue

        if key not in result:
            result[key] = value
            continue

        if type(result[key]) != type(value):  # pylint: disable=unidiomatic-typecheck
            raise ValueError("Can't replace {!r} with {!r}".format(result[key], value))

        if isinstance(value, dict):
            result[key] = overlay(result[key], value)
        else:
            result[key] = value

    return result