Some user feedback:
After having tried to upgrade a 200 kloc code base from typing.TypeAlias to type just to have to revert it all I’m not sure how we can deprecate TypeAlias. Here’s a tagged union type definition, which we use together with pydantic, among other things. We probably have 100 of these in our code base, each with perhaps 2-10 union members.
ChatMessage: TypeAlias = Annotated[
TextMessage | AudioMessage,
pydantic.Field(discriminator='message_type')
]
We will use this TypeAlias to annotate e.g. variables:
msg: ChatMessage
To do type narrowing:
if isinstance(obj, ChatMessage):
...
And to do runtime parsing using Pydantic:
ta: pydantic.TypeAdapter[ChatMessage] = pydantic.TypeAdapter(ChatMessage)
msg = ta.validate_python({"message_type": "message", ...}) # parse into target type or fail
Issues when switching to type aliases:
isinstancedoesn’t work.- All code that does runtime work on e.g. unions now need to manually flatten unions to remove
typealiases (see the end of the post for a helper I wrote).
If we didn’t have TypeAlias anymore we would have to define every type twice, something like:
type ChatMessage = Annotated[
TextMessage | AudioMessage,
pydantic.Field(discriminator='message_type')
]
ChatMessageTuple = (TextMessage, AudioMessage)
And, depending on the use case, use one or the other. Examples:
# annotating e.g. variables:
msg: ChatMessage # must use type alias
# type narrowing:
if isinstance(obj, ChatMessageTuple): # must use tuple
...
# type-directed parsing (probably doesn't type check):
ta: pydantic.TypeAdapter[ChatMessage] = pydantic.TypeAdapter(ChatMessageTuple)
I suspect that the pydantic.TypeAdapter wouldn’t work at all (without unsafe casts), as it needs to relate a static type (the return type of validate_python()) and a runtime type (the argument of TypeAdapter()), which isn’t possible if the type has been split in two as above, not even in theory.