Search code examples
python-3.xpython-dataclassespython-class

Implementing Typescript interfaces in Python


I'm looking for some advice on what's the best way of implementing a set of data value only 'interfaces' in Python that are equivalent to their typescript counterpart (we've got a project where we use both, and want to enforce a consistent interface for their communication, which would be via serialising the python into json to pull into the TS component)

The interfaces will be compositions to keep things modular and simple.

Given a set of TS interfaces defined as:

interface TestOutput {
  phantom: string
  testDateTime: datetime
  author: string
  result: boolean
  report_summaryFile?: string // the '?' means this field is optional
  // ... more values
  series: Array<Series>
  soloImages: Array<Images>
}

interface Series {
  number: number
  filter: string
  kernel: string
  // ... more values
  images: Array<TestImage>
}

I was thinking of using dataclasses and doing the following:

from dataclasses import dataclass
from typing import List
import datetime

@dataclass
class TestSeries:
    seriesNum: int 
    modality: str 
    description: str = ''

@dataclass
class TestOutput:
    phantom: str 
    testDateTime: datetime.datetime
    author: str 
    result: bool
    series: List[TestSeries]
    soloImages: List[Images]
    report_summaryFile: str = ''

Is dataclasses the best approach for this?


Solution

  • pydantic is a good library.
    I did something similar, but only for dataclasses - ValidatedDC:

    from dataclasses import dataclass
    from typing import List
    
    from validated_dc import ValidatedDC
    import json
    
    
    @dataclass
    class Series(ValidatedDC):
        series_num: int
        modality: str
        description: str = ''
    
    
    @dataclass
    class Output(ValidatedDC):
        phantom: str
        date_time: str
        author: str
        result: bool
        series: List[Series]
        report_summary_file: str = ''
    
    
    # For example, by API we got a JSON string:
    input_json_string = '''
        {
            "phantom": "test_phantom",
            "date_time": "2020.01.01",
            "author": "Peter",
            "result": true,
            "series": [{
                "series_num": 1,
                "modality": "test_modality"
            }]
        }
    '''
    
    # Load the string into the dictionary:
    input_data = json.loads(input_json_string)
    
    # Then create a dataclass to check the types of values and for the
    # convenience of further work with data:
    output = Output(**input_data)
    
    # Since valid data were obtained, there are no errors
    assert output.get_errors() is None
    
    # Let's say we got data with an error:
    input_data['series'][0]['series_num'] = '1'  # The string is not an integer!
    
    output = Output(**input_data)
    assert output.get_errors()
    print(output.get_errors())
    # {
    #    'series': [
    #        InstanceValidationError(
    #            value_repr="{'series_num': '1', 'modal...}",
    #            value_type=<class 'dict'>,
    #            annotation=<class '__main__.Series'>, exception=None,
    #            errors={
    #                'series_num': [
    #                    BasicValidationError(
    #                        value_repr='1', value_type=<class 'str'>,
    #                        annotation=<class 'int'>, exception=None
    #                    )
    #                ]
    #            }
    #        ),
    #        ListValidationError(
    #            item_index=0, item_repr="{'series_num': '1', 'modal...}",
    #            item_type=<class 'dict'>, annotation=<class '__main__.Series'>
    #        )
    #    ]
    # }
    

    See here for more details:
    https://github.com/EvgeniyBurdin/validated_dc