ts2python Documentation

ts2python is a transpiler that converts TypeScript-interfaces to Python TypedDict-classes and provides runtime json-validation against those interfaces/TypedDicts.

ts2python is licensed under the Apache-2.0 open source license. The source code can be cloned freely from: https://github.com/jecki/ts2python

Installation

ts2python can be installed as usual with Python’s package-manager “pip”:

$ pip install ts2python

The ts2python-transpiler requires at least Python Version 3.8 to run. However, the output that ts2python produces is backwards compatible with Python 3.7, unless otherwise specified (see below). The only dependency of ts2python is the parser-generator DHParser.

Generating Python-pendants for Typescript-interfaces is as simple as calling:

$ ts2python interfaces.ts

and then importing the generated interfaces.py by:

from interfaces import *

For every typescript interface in the interfaces.ts file the generated Python module contains a TypedDict-class with the same name that defines the same data structure as the Typescript-interface. Typescript-data serialized as json can simply be deserialized by Python-code as long as you know the type of the root data structure beforehand, e.g.:

import json
request_msg: RequestMessage = json.loads(input_data)

The only requirement is that the root-type of the json-data is known beforehand. Everything else simply falls into place.

Backwards compatibility

ts2python tries to be as backwards compatible as possible. To run ts2python you need at least Python version 3.8. The code ts2python generates is backwards compatible down to version 3.7. If you do not need to be compatible with older versions, you can use the –compatibility [VERSION] switch to generate code for newer versions only. Usually, this code is a bit cleaner than the fully compatible code, e.g.:

$ ts2python --compatibility 3.11 [FILENAME.ts]

In order to achieve full conformity with most type-checkers, it is advisable to use compatibility level 3.11 and also add the -a toplevel-switch to always turn anonymous TypeScript-interfaces into top-level classes, rather than locally defined classes, which is not allowed for Python’s TypedDict, although it should be (in my IMHO) and works perfectly well - except that type-checkers like pylance emit an error-message.

With compatibility-level 3.11 and above, the generated code does not need to use ts2python’s “typeddict_shim”-compatibility layer, any more. This greatly simplifies the import-block at the beginning of the generated code and eliminates one of two dependencies of the generated code on the ts2python-package.

The other remaining dependency “singledispatch_shim” can be removed from the generated code by hand, if there are no single-dispatch functions in the code that dispatch on a forward-referenced type, i.e. a type that is denoted in the code by a string containing the type-name rather than the type-name directly. (It’s a current limitation of functools.singledispatch that it cannot handle forward references.)

Current Limitations

Presently, ts2python is mostly limited to Typescript-Interfaces that do not contain any methods. The language server-protocol-definitions can be transpiled successfully.

Hacking ts2python

Hacking ts2python is not easy. (Sorry!) The reason is that ts2python was primarily designed for a relatively limited application case, i.e. transpiling interface definitions. In order to keep things simple, the abstract syntax tree (AST) from the TypeScript source is directly converted to Python code, rather than transforming the TypeScript-AST to a Python-AST first.

Adding such a tree-to-tree-transformation before the Python code generation stage certainly would have made sense - among other things, because some components need to be re-ordered, since Python does not know anonymous classes/interfaces. However, for the above mentioned restricted purpose this appeared to me like overengineering. Meanwhile, I have come to regret that, because it makes adding more features harder.

In order to keep track of code snippets that must be reordered or of names and scopes that need to be completed or filled in later or, more generally, for any task that cannot be completed locally, when transforming a particular node of the AST, the present implementation keeps several different stacks in instance-variables of the ts2pythonCompiler-object, namely, known_types, local_classes, base_classes, obj_name, scope_type, optional_keys. Lookout for where those stacks are used when you to change something in the ts2python-source yourself!

Indices and tables