We use lots of TypeScript at work, and ‘man, I wish this existed in Python’ is not the impression TypeScript’s box of tricks has given me.
In my experience, if your logic is dynamic enough to need, for instance, conditional types, then it’s probably not worth trying to type it statically.
I’m concerned that these sorts of tools add more ways to do the same thing, in usually a worse way.
For instance,
@dataclass
class A:
dogs: list[str]
@dataclass
class B:
dogs: int
def f(y: A | B):
...
is the sort of function that might like to use conditional types in its implementation, but it should generally instead be written
@dataclass
class Base:
dogs: list[str] | int
@dataclass
class A(Base):
dogs: list[str]
@dataclass
class B(Base):
dogs: int
def f(y: Base):
...
(and then you take a quick look at Base, realise that dogs are different things, and fix your dodgy structure.)
TypeScript code often ends up like this, defining abstractions as up of disparate classes, rather than using inheritance hierarchies, because it gives you a large tool-set for working with the former. However, it’s generally poor architecture, because (contravening the DIP), it makes the general case depend on the specifics, coupling them all together.
In TypeScript, that’s somewhat inevitable, because it has to work with (a) JavaScript’s funny ideas about Objects, and (b) the general tendency of JavaScript code to end up as a bit of a tangled mess, needing type system super-weapons to navigate. Python doesn’t have the same issues, and so I’d be cautious of adopting ideas from there.
Static typing is inherently conservative: there will always be programs that won’t type check, yet are logically correct. At some point, the expressiveness gained from new type system intricacies is not worth the complication they bring. The language might not be quite at that point yet, but I don’t think it’s far off, and I’d worry this proposal takes it over that line.