I also think that Eric's post is good.

However, I don't think that angle brackets are as viable as square brackets. First, it seems less consistent than square brackets -- I agree with Kevin here. There also seems to be some potential for ambiguity: Callable<T>[[T], T], for example, is already a valid expression, where < and > are treated as binary operators. Finally, square brackets would naturally support splitting long definitions into multiple lines (in this simple example it's not necessary to use multiple lines, but in more complex examples it can be helpful):

class C[
    T,
    S,
](...):
    ...

Supporting this with angle brackets seems kind of hard (especially within expressions, where there is ambiguity with existing binary operators):

class C<
    T,
    S,
>(...):
    ...

I guess we could somehow support this, but it could be hard to justify the extra complexity in a PEP.

The alternative of using backslashes looks pretty bad, I think:

class C< \
    T, \
    S, \
>(...):
    ...

Of the Eric's proposed ways of expressing type aliases, only the idea with a new keyword seems practical to me if we use square brackets. This is the first alternative that Eric mentioned:

ListOrDict<T1, T2> = List<T2> | Dict<T1, T2>

If we'd use square brackets instead, this would be syntactically identical to an indexed assignment, which wouldn't define the type alias, T1 or T2, and thus wouldn't work at runtime:

ListOrDict[T1, T2] = List[T2] | Dict[T1, T2]

The second idea is similarly ambiguous and looks like a regular generic type annotation when using square brackets, unless TypeAlias is a keyword. Again, T1 and T2 won't be defined at runtime:

ListOrDict: TypeAlias[T1, T2] = List[T2] | Dict[T1, T2]

The third idea has the same issue if using square brackets -- it already has different meaning currently and doesn't define T1 or T2:

ListOrDict: TypeAlias = type_alias[T1, T2](List[T2] | Dict[T1, T2])

If we introduce a new keyword (only treated as a keyword in this context), there would be no ambiguity and everything would work as expected, I think -- even with square brackets:

alias ListOrDict[T1, T2] = List[T2] | Dict[T1, T2]

Jukka