With just your one example it's not clear which types other than literally {} you'd be okay with dropping: for example, do you need to keep or drop the object type? What about the unknown type? I'm not going to worry about this too much; just know that you should probably run through a suite of possible edge cases and figure out if the behavior of any type you come up with is acceptable.
Anyway, one possible implementation that doesn't rely on importing external libraries is:
type DropEmpty<T> =
{ [K in keyof T as keyof T[K] extends never ? never : K]: T[K] };
This is using key remapping in mapped types to suppress any properties matching a certain criterion, by remapping the key in question to never.
You can verify that this behaves as desired for your example:
type Result = DropEmpty<Foo>
/*type Result = {
y: {
bar: string;
};
} */
Playground link to code