I agree with your conclusion, but I think the forward refs issue is more annoying than you're giving it credit (not that the fix should break stuff). Type hints happen at a higher level, so things are more strict.
class A:
def x(self) -> B: # doesn't work
return B() # works
class B:
pass
I find this annoying enough that I've been using the future annotations.
Generally speaking that's considered unpythonic though. Default values are evaluated exactly once, at function definition time. So if you do this, then your default B() will always be the exact same instance of the B class -- literally the same object in memory -- every time you call the function. That's rarely what you want; you usually want the default to be a fresh instance of B(). So instead, the pythonic way is for the default to be None, and then to convert those Nones into their actual defaults in the top of the function/method. This then has the added benefit of playing more nicely with subclassing (as well as composition), because instead of needing to bubble the defaults up the call chain, you simply pass down None and let the implementing function worry about resolving it into the actual default.
I make way more small, immutable classes than the average Python developer. I've never had the reference issue with defaults and always have it with hints.