Python equivalent of Typescript interface Python equivalent of Typescript interface python python

Python equivalent of Typescript interface


For the code completion and type hinting in IDEs, just add static typing for the Person and Address classes and you are already good to go. Assuming you use the latest python3.6, here's a rough equivalent of the typescript classes from your example:

# spam.pyfrom typing import Optional, Sequenceclass Address:    street: str    housenumber: int    housenumber_postfix: Optional[str]    def __init__(self, street: str, housenumber: int,                  housenumber_postfix: Optional[str] = None) -> None:        self.street = street        self.housenumber = housenumber        self.housenumber_postfix = housenumber_postfixclass Person:    name: str    adresses: Sequence[Address]    def __init__(self, name: str, adresses: Sequence[str]) -> None:        self.name = name        self.adresses = adressesperson = Person('Joe', [    Address('Sesame', 1),     Address('Baker', 221, housenumber_postfix='b')])  # type: Person

I suppose the boilerplate you mentioned emerges when adding the class constructors. This is indeed inavoidable. I would wish default constructors were generated at runtime when not declared explicitly, like this:

class Address:    street: str    housenumber: int    housenumber_postfix: Optional[str]class Person:    name: str    adresses: Sequence[Address]if __name__ == '__main__':    alice = Person('Alice', [Address('spam', 1, housenumber_postfix='eggs')])    bob = Person('Bob', ())  # a tuple is also a sequence

but unfortunately you have to declare them manually.


Edit

As Michael0x2a pointed out in the comment, the need for default constructors is made avoidable in python3.7 which introduced a @dataclass decorator, so one can indeed declare:

@dataclassclass Address:    street: str    housenumber: int    housenumber_postfix: Optional[str]@dataclassclass Person:    name: str    adresses: Sequence[Address]

and get the default impl of several methods, reducing the amount of boilerplate code. Check out PEP 557 for more details.


I guess you could see stub files that can be generated from your code, as some kind of interface files:

$ stubgen spam  # stubgen tool is part of mypy packageCreated out/spam.pyi

The generated stub file contains the typed signatures of all non-private classes and functions of the module without implementation:

# Stubs for spam (Python 3.6)## NOTE: This dynamically typed stub was automatically generated by stubgen.from typing import Optional, Sequenceclass Address:    street: str    housenumber: int    housenumber_postfix: Optional[str]    def __init__(self, street: str, housenumber: int, housenumber_postfix: Optional[str]=...) -> None: ...class Person:    name: str    adresses: Sequence[Address]    def __init__(self, name: str, adresses: Sequence[str]) -> None: ...person: Person

These stub files are also recognized by IDEs and if your original module is not statically typed, they will use the stub file for type hints and code completion.


Python 3.6 added a new implementation of namedtuple that works with type hints, which removes some of the boilerplate required by the other answers.

from typing import NamedTuple, Optional, Listclass Address(NamedTuple):    street: str    housenumber: int    housenumberPostfix: Optional[str] = Noneclass Person(NamedTuple):    name: str    adresses: List[Address]person = Person(    name='Joe',    adresses=[        Address(street='Sesame', housenumber=1),        Address(street='Baker', housenumber=221, housenumberPostfix='b'),    ],)

Edit: NamedTuples are immutable, so be aware that you can't use this solution if you want to modify the fields of your objects. Changing the contents of lists and dicts is still fine.


A simple solution I found (that doesn't require Python 3.7) is to use SimpleNamespace:

from types import SimpleNamespace as NSfrom typing import Optional, Listclass Address(NS):    street: str    housenumber: int    housenumber_postfix: Optional[str]=Noneclass Person(NS):    name: str    addresses: List[Address]person = Person(    name='Joe',    addresses=[        Address(street='Sesame', housenumber=1),        Address(street='Baker', housenumber=221, housenumber_postfix='b')    ])
  • This works in Python 3.3 and higher
  • The fields are mutable (unlike NamedTuple solution)
  • Code completion seems to work flawlessly in PyCharm but not 100% in VSCode (raised an issue for that)
  • Type checking in mypy works, but PyCharm does not complain if I e.g do person.name = 1

If anyone can point out why Python 3.7's dataclass decorator would be better I would love to hear.