With more complex types, I've found it isn't necessary to do anything more complex than specifying the element type of a list or dict most of the time. I have typehinted lambda function arguments before, but just using the plain Callable typehint is usually enough. When I forget if I need to use an Iterator or an Iterable (which is every time), then I just try one and run the type checker and change it to the other if i guessed incorrectly.
Type checking Python can become complex if you expect to be able to express everything in the typehints, but Python's type system isn't powerful enough for that. I feel its strength is that it lets you add as much detail to the types as is convenient.
it's worth exploring some of the other type checkers as well, since they make different tradeoffs - in particular, microsoft's pyright[2] (written in typescript!) can run incrementally within vscode, and tends to add new and experimentally proposed typing PEPs faster than we do.
[1] https://github.com/google/pytype/blob/main/docs/developers/i...