Flask & Quart Example¶
This example shows how to generate typescript types from function return typehints in the Flask/Quart framework. We will use quart in this example, but the same approach can be used for flask as well.
Setup¶
Let’s assume we have a simple quart app with some endpoint that returns a json response. We want to generate typescript types for the responses which we have already typed, as we are on exemplary behavior today.
This might look like this:
from quart import Quart
from typing import TypedDict
app = Quart(__name__)
class HelloResponse(TypedDict):
hello: str
class ComplexResponse(TypedDict):
other: HelloResponse
age: int
is_student: bool
@app.route("/hello/<name>")
async def hello_json(name: str) -> HelloResponse:
return {"hello": name}
@app.route("/complex/<name>")
async def complex_json(name: str) -> ComplexResponse:
return {"other": {"hello": name}, "age": 20, "is_student": True}
Typehints for the quart routes¶
We can now iterate all routes and their view functions, this is builtin into quart. We can then extract the return type of the view function and generate a typescript type from it.
from typing import get_type_hints, Any
from quart.wrappers import Response
# Iterate through all the quart routes with their function rules
# and store the return typehint such as the route string
routes = []
for (name, func), rule in zip(app.view_functions.items(), app.url_map.iter_rules()):
# We get the return typehint of the function
return_typehint = get_type_hints(func).get("return", {})
if not return_typehint or return_typehint is Response:
return_typehint = Any
# We store the route and the return typehint
# in theory we can extract more information here but this
# is enough for the example
routes.append({"route": str(rule), "return": return_typehint})
routes
[{'route': '/static/<path:filename>', 'return': typing.Any},
{'route': '/hello/<name>', 'return': __main__.HelloResponse},
{'route': '/complex/<name>', 'return': __main__.ComplexResponse}]
Generating the typescript types¶
We have done all the basic work we can now and can generate the typescript types for these return typehints. First we resolve all common dependencies of the return types and then generate a map of all routes and their types.
import py2ts
import py2ts.data
# Generate typescript dependencies of the types
ts_str = ""
ts_types = [py2ts.generate_ts(route["return"]) for route in routes]
ts_str += py2ts.data.ts_reference_str(ts_types)
# Generate mapping of routes to types (using standard string formatting of ts_type)
ts_str += "export type Routes = {\n"
for route, ts_type in zip(routes, ts_types):
if isinstance(ts_type, py2ts.data.TSInterface):
ts_str += f'\t"{route["route"]}": {ts_type.name};\n'
else:
ts_str += f'\t"{route["route"]}": {ts_type};\n'
ts_str += "};"
print(ts_str)
export interface ComplexResponse {
other: HelloResponse;
age: number;
is_student: boolean;
}
export interface HelloResponse {
hello: string;
}
export type Routes = {
"/static/<path:filename>": any;
"/hello/<name>": HelloResponse;
"/complex/<name>": ComplexResponse;
};