But once you have type validation i don't really see the use case anymore. Common example of how silly this is is functions that have a special case for handling lists of length 1 by allowing the object to be passed in without being part of a list, eg Union[List[Foo], Foo]. Now you need extra code to handle that special case in your function and the type annotation becomes more verbose. Much easier to just enforce List[Foo] as the only way to call your function and let the caller call you function as do_stuff([x]) if they only have a single item x. The IDE will complain instantly if you try to call it as do_stuff(x) so there is no risk to accidentally call it like that.
But certainly, it doesn’t remove the need for good test coverage. Now, if you want to argue for less testing, that is a different pitch.
Factory methods are usually better, and I believe what the more modern parts of the standard library have tried to use, but they don't work as well for this specific case.
Once you start doing this in python -- beyond any low complexity usage -- you're better off capturing your arguments in their own object and passing this to the function as a single argument. This object can then enforce whatever you want, and your function can now be a class method.
And then you can build a factory for this object that accepts a few args, maybe some keyword parameters. But, once that gets reasonably complex, just rinse and repeat. It's objects, methods and factories all the way down. And unlimited billable hours.
It requires among other things, a unique and toilsome coding style, constructor overloading (which isn't a first class citizen of python), and you only get late runtime validation (which means, for example, you can easily miss type validation on only one side of a conditional).
Something like pytype detects such errors without any code modifications.