@@ -820,22 +820,30 @@ reveal_type(C().c) # revealed: int
820820
821821### Inheritance of class/instance attributes
822822
823- #### Instance variable defined in a base class
824-
825823``` py
826824class Base :
827- declared_in_body: int | None = 1
825+ attribute: int | None = 1
826+
827+ redeclared_with_same_type: str | None
828+ redeclared_with_narrower_type: str | None
829+ redeclared_with_wider_type: str | None
830+
831+ overwritten_in_subclass_body: str
832+ overwritten_in_subclass_method: str
828833
829- base_class_attribute_1: str | None
830- base_class_attribute_2: str | None
831- base_class_attribute_3: str | None
834+ undeclared = " base"
832835
833836 def __init__ (self ) -> None :
834- self .defined_in_init: str | None = " value in base"
837+ self .pure_attribute: str | None = " value in base"
838+
839+ self .pure_overwritten_in_subclass_body: str = " value in base"
840+ self .pure_overwritten_in_subclass_method: str = " value in base"
841+
842+ self .pure_undeclared = " base"
835843
836844class Intermediate (Base ):
837845 # Redeclaring base class attributes with the *same *type is fine:
838- base_class_attribute_1 : str | None = None
846+ redeclared_with_same_type : str | None = None
839847
840848 # Redeclaring them with a *narrower type* is unsound, because modifications
841849 # through a `Base` reference could violate that constraint.
@@ -847,22 +855,70 @@ class Intermediate(Base):
847855 # enabled by default can still be discussed.
848856 #
849857 # TODO : This should be an error
850- base_class_attribute_2 : str
858+ redeclared_with_narrower_type : str
851859
852860 # Redeclaring attributes with a *wider type* directly violates LSP.
853861 #
854862 # In this case, both mypy and pyright report an error.
855863 #
856864 # TODO : This should be an error
857- base_class_attribute_3: str | int | None
865+ redeclared_with_wider_type: str | int | None
866+
867+ # TODO : This should be an `invalid-assignment` error
868+ overwritten_in_subclass_body = None
869+
870+ # TODO : This should be an `invalid-assignment` error
871+ pure_overwritten_in_subclass_body = None
872+
873+ undeclared = " intermediate"
874+
875+ def set_attributes (self ) -> None :
876+ # TODO : This should be an `invalid-assignment` error
877+ self .overwritten_in_subclass_method = None
878+
879+ # TODO : This should be an `invalid-assignment` error
880+ self .pure_overwritten_in_subclass_method = None
881+
882+ self .pure_undeclared = " intermediate"
858883
859884class Derived (Intermediate ): ...
860885
861- reveal_type(Derived.declared_in_body) # revealed: int | None
886+ reveal_type(Derived.attribute) # revealed: int | None
887+ reveal_type(Derived().attribute) # revealed: int | None
888+
889+ reveal_type(Derived.redeclared_with_same_type) # revealed: str | None
890+ reveal_type(Derived().redeclared_with_same_type) # revealed: str | None
891+
892+ # TODO : It would probably be more consistent if these were `str | None`
893+ reveal_type(Derived.redeclared_with_narrower_type) # revealed: str
894+ reveal_type(Derived().redeclared_with_narrower_type) # revealed: str
895+
896+ # TODO : It would probably be more consistent if these were `str | None`
897+ reveal_type(Derived.redeclared_with_wider_type) # revealed: str | int | None
898+ reveal_type(Derived().redeclared_with_wider_type) # revealed: str | int | None
899+
900+ # TODO : Both of these should be `str`
901+ reveal_type(Derived.overwritten_in_subclass_body) # revealed: Unknown | None
902+ reveal_type(Derived().overwritten_in_subclass_body) # revealed: Unknown | None | str
903+
904+ # TODO : Both of these should be `str`
905+ reveal_type(Derived.overwritten_in_subclass_method) # revealed: str
906+ reveal_type(Derived().overwritten_in_subclass_method) # revealed: str | Unknown | None
907+
908+ reveal_type(Derived().pure_attribute) # revealed: str | None
909+
910+ # TODO : This should be `str`
911+ reveal_type(Derived().pure_overwritten_in_subclass_body) # revealed: Unknown | None | str
912+
913+ # TODO : This should be `str`
914+ reveal_type(Derived().pure_overwritten_in_subclass_method) # revealed: Unknown | None
862915
863- reveal_type(Derived().declared_in_body) # revealed: int | None
916+ # TODO : Both of these should be `Unknown | Literal["intermediate", "base"]`
917+ reveal_type(Derived.undeclared) # revealed: Unknown | Literal["intermediate"]
918+ reveal_type(Derived().undeclared) # revealed: Unknown | Literal["intermediate"]
864919
865- reveal_type(Derived().defined_in_init) # revealed: str | None
920+ # TODO : This should be `Unknown | Literal["intermediate", "base"]`
921+ reveal_type(Derived().pure_undeclared) # revealed: Unknown | Literal["intermediate"]
866922```
867923
868924## Accessing attributes on class objects
0 commit comments