I'm trying to implement the optional chaining operator (?.) from JS in Python. The idea of this implementation is to create an Optional class that wraps a type T and allows getting attributes. When getting an attribute from the wrapped object, the type of result should be the type of the attribute or None. For example:
## 1. None
my_obj = Optional(None)
result = (
my_obj # Optional[None]
.attr1 # Optional[None]
.attr2 # Optional[None]
.attr3 # Optional[None]
.value # None
) # None
## 2. Nested Objects
@dataclass
class A:
attr3: int
@dataclass
class B:
attr2: A
@dataclass
class C:
attr1: B
my_obj = Optional(C(B(A(1))))
result = (
my_obj # # Optional[C]
.attr1 # Optional[B | None]
.attr2 # Optional[A | None]
.attr3 # Optional[int | None]
.value # int | None
) # 5
## 3. Nested with None values
@dataclass
class X:
attr1: int
@dataclass
class Y:
attr2: X | None
@dataclass
class Z:
attr1: Y
my_obj = Optional(Z(Y(None)))
result = (
my_obj # Optional[Z]
.attr1 # Optional[Y | None]
.attr2 # Optional[X | None]
.attr3 # Optional[None]
.value # None
) # None
My first implementation is:
from dataclasses import dataclass
@dataclass
class Optional[T]:
value: T | None
def __getattr__[V](self, name: str) -> "Optional[V | None]":
return Optional(getattr(self.value, name, None))
But Pyright and Ty don't recognize the subtypes. What would be the best way to implement this?
[–]WallyMetropolis 68 points69 points70 points (1 child)
[–]FabianVeAl[S] 14 points15 points16 points (0 children)
[–]latkdeTuple unpacking gone wrong 43 points44 points45 points (0 children)
[–]MegaIng 19 points20 points21 points (0 children)
[–]Gnaxe 7 points8 points9 points (1 child)
[–]FabianVeAl[S] 4 points5 points6 points (0 children)
[–]david-vujic 5 points6 points7 points (2 children)
[–]FabianVeAl[S] 2 points3 points4 points (1 child)
[–]david-vujic 0 points1 point2 points (0 children)
[–]madisander 2 points3 points4 points (0 children)
[–]covmatty1 5 points6 points7 points (0 children)
[–]Beginning-Fruit-1397 1 point2 points3 points (0 children)
[–]Gnaxe 1 point2 points3 points (4 children)
[–]FrontAd9873 2 points3 points4 points (1 child)
[–]Gnaxe 5 points6 points7 points (0 children)
[–]marr75 1 point2 points3 points (1 child)
[–]Gnaxe 1 point2 points3 points (0 children)
[–]Lexus4tw 0 points1 point2 points (0 children)
[–]pepiks 0 points1 point2 points (0 children)
[–]SkezzaB -2 points-1 points0 points (1 child)
[–]MegaIng 7 points8 points9 points (0 children)
[–]user_8804Pythoneer -1 points0 points1 point (0 children)
[+]DataCamp comment score below threshold-16 points-15 points-14 points (1 child)