Now type aliases for Non-generic types are almost useless, like the one showed in the PEP 484
Url = str
def retry(url: Url, retry_count: int) -> None: ...
With the modern syntax, we would have:
type Url = str
def retry(url: Url, retry_count: int) -> None: ...
Which won’t do anything in static analysis:
test: str = 'not_an_url'
test1 = retry(test) # Doesn't give a static warning
test = 'not_an_url'
test2 = retry(test) # Doesn't give a warning, but may be nice based on --strict
test: Url = 'not_an_url'
test3 = retry(test) # Doesn't give a static warning (It shouldn't)
So in my opinion we are better off just omitting the alias, and ensure that the variable name is descriptive. Or better use typing.NewType.
def retry(url: str, retry_count: int) -> None: ... # option 1
Url = NewType("Url", str)
def retry(url: Url, retry_count: int) -> None: ... # option 2
Given that I can’t see any use-case of an alias in a Non-generic type, please tell me if I’m missing something. My proposal would be, using the keyword type
for defining Newtypes in Non generics:
type Url = str # interpret this as NewType , it can be subclassed
type UrlList = list[Url] # interpret this as Alias (TypeAliasType), it can't be subclassed
Considering that one can be sub-classed and the other not:
class Url(str): pass # This is valid
class UrlList(list[Url]): pass # This is not valid
This behavior will be more in line with what I would expect when using the word “type”. And also gives a real usage to the keyword type
with non-generic classes.
PD: This topic was already discussed here #45186, where I questioned this behavior, before i figured out how and why it works like that. This post intends to be a proposal after having a slightly deeper understanding on the topic. In any case if is too similar for the rules of the forum, feel free to close it or tell me to do so.