Thanks for this very good question!
It's practically okay as I tried, as the following code can pass mypy type checking:
```
from typing import List, NewType
Matrix = NewType('Matrix', List[List[int | float]])
A: Matrix = Matrix([[1, 2, 3], [4, 5, 6]])
```
But based on the Python documents, the original purpose of these two type hints are slightly different:
"Recall that the use of a type alias declares two types to be equivalent to one another. Doing type Alias = Original will make the static type checker treat Alias as being exactly equivalent to Original in all cases. This is useful when you want to simplify complex type signatures.
In contrast, NewType declares one type to be a subtype of another. Doing Derived = NewType('Derived', Original) will make the static type checker treat Derived as a subclass of Original, which means a value of type Original cannot be used in places where a value of type Derived is expected. This is useful when you want to prevent logic errors with minimal runtime cost."
In practice, I would use TypeAlias when I just wanna simplify a complex type hint, and use the NewType to create a distinct type from a simple existing type.
Reference here: