I recently added type annotations to my Python 3.7 project and found it very helpful. Just the process of adding the annotations caught a handful of bugs!
Some things I found useful:
1. from __future__ import annotations
As I mentioned in another comment, this lets you write annotations naturally without worrying about when something gets defined.
2. typing.TYPE_CHECKING is only True while type checking.
This allows you to conditionally import files that would otherwise cause a circular dependency so that you can use the symbols for annotations.
3. def foo(bar: str = None) -> str
If an argument defaults to None, then mypy will recognize that its type is actually Optional[whatever]. So in the example above, bar is an Optional[str]. (I'm not sure if this is mypy-specific.) Optional[T] is equivalent to Union[T, None].
Annotations in comments or in type stubs are supported in python2 (you can look at typeshed for typing.Text and conditional py2/3 stuff). There's also some other cases, but they're...unique.
Some things I found useful:
1. from __future__ import annotations
As I mentioned in another comment, this lets you write annotations naturally without worrying about when something gets defined.
2. typing.TYPE_CHECKING is only True while type checking.
This allows you to conditionally import files that would otherwise cause a circular dependency so that you can use the symbols for annotations.
3. def foo(bar: str = None) -> str
If an argument defaults to None, then mypy will recognize that its type is actually Optional[whatever]. So in the example above, bar is an Optional[str]. (I'm not sure if this is mypy-specific.) Optional[T] is equivalent to Union[T, None].
4. You can make typedefs easily.
MyType = Union[Sequence[str], Dict[str, int]]
Makes writing complicated annotations easier.