>why not a protocol type

it was a sin that python's type system was initially released as a nominal type system. they should have been the target from day one.

being unable to just say "this takes anything that you can call .hello() and .world() on" was ridiculous, as that was part of the ethos of the dynamically typed python ecosystem. typechecking was generally frowned upon, with the idea that you should accept anything that fit the shape the receiving code required. it allowed you to trivially create resource wrappers and change behaviors by providing alternate objects to existing mechanisms. if you wanted to provide a fake file that read from memory instead of an actual file, it was simple and correct.

the lack of protocols made hell of these patterns for years.

I disagree. I think, if the decision was made today, it probably would have ended up being structural, but the fact that it isn't enables (but doesn't necessarily force) Python to be more correct than if it weren't (whereas forced structural typing has a certain ceiling of correctness).

Really it enabled the Python type system to work as well as it does, as opposed to TypeScript, where soundness is completely thrown out except for some things such as enums

Nominal typing enables you to write `def ft_to_m(x: Feet) -> Meters: and be relatively confident that you're going to get Feet as input and Meters as output (and if not, the caller who ignored your type annotations is okay with the broken pieces).

The use for protocols in Python in general I've found in practice to be limited (the biggest usefulness of them come from the iterable types), when dealing with code that's in a transitional period, or for better type annotations on callables (for example kwargs, etc).

TypeScript sacrificed soundness to make it easier to gradually type old JS code and to allow specific common patterns. There is no ceiling for correctness of structural typing bar naming conflicts.

>The use for protocols in Python in general I've found in practice to be limited (the biggest usefulness of them come from the iterable types)

Most Python's dunder methods make it so you can make "behave alike" objects for all kinds of behaviors, not just iterables

AFAIK, Python is missing a fully-featured up to date centralized documentation on how to use type annotations.

The current docs are "Microsoft-like", they have everything, spread through different pages, in different hierarchies, some of them wrong, and with nothing telling you what else exists.