Skip to content

Commit 22bf717

Browse files
committed
Better example based on actual api usage
1 parent 0ae23cf commit 22bf717

File tree

8 files changed

+117
-105
lines changed

8 files changed

+117
-105
lines changed

README.md

Lines changed: 55 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -18,65 +18,77 @@ $ pip install pydantic-to-typescript
1818
|‑‑json2ts‑cmd|optional, the command used to invoke json2ts. The default is 'json2ts'. Specify this if you have it installed in a strange location and need to provide the exact path (ex: /myproject/node_modules/bin/json2ts)|
1919
---
2020
### Usage
21-
pydantic2ts/examples/pydantic_models.py:
21+
Define your pydantic models (ex: /backend/api.py):
2222
```python
23-
from pydantic import BaseModel, Extra
24-
from enum import Enum
25-
from typing import List, Dict
23+
from fastapi import FastAPI
24+
from pydantic import BaseModel
25+
from typing import List, Optional
2626

27+
api = FastAPI()
2728

28-
class NoExtraProps:
29-
extra = Extra.forbid
29+
class LoginCredentials(BaseModel):
30+
username: str
31+
password: str
3032

33+
class Profile(BaseModel):
34+
username: str
35+
age: Optional[int]
36+
hobbies: List[str]
3137

32-
class Sport(str, Enum):
33-
football = 'football'
34-
basketball = 'basketball'
38+
class LoginResponseData(BaseModel):
39+
token: str
40+
profile: Profile
3541

36-
37-
class Athlete(BaseModel):
38-
name: str
39-
age: int
40-
sports: List[Sport]
41-
Config = NoExtraProps
42-
43-
44-
class Team(BaseModel):
45-
name: str
46-
sport: Sport
47-
athletes: List[Athlete]
48-
Config = NoExtraProps
49-
50-
51-
class League(BaseModel):
52-
cities: Dict[str, Team]
53-
Config = NoExtraProps
42+
@api.post('/login/', response_model=LoginResponseData)
43+
def login(body: LoginCredentials):
44+
profile = Profile(**body.dict(), age=72, hobbies=['cats'])
45+
return LoginResponseData(token='very-secure', profile=profile)
5446
```
55-
Command-line:
47+
Execute the command for converting these models into typescript definitions:
5648
```bash
57-
$ pydantic2ts --module pydantic2ts.examples.pydantic_models --output output.ts
58-
```
59-
output.ts:
49+
$ pydantic2ts --module backend.api --output ./frontend/apiTypes.ts
6050
```
51+
The models are now defined in typescript...
52+
```ts
6153
/* tslint:disable */
6254
/**
6355
/* This file was automatically generated from pydantic models by running pydantic2ts.
6456
/* Do not modify it by hand - just update the pydantic models and then re-run the script
6557
*/
6658

67-
export interface Athlete {
68-
name: string;
69-
age: number;
70-
sports: ("football" | "basketball")[];
59+
export interface LoginCredentials {
60+
username: string;
61+
password: string;
62+
}
63+
export interface LoginResponseData {
64+
token: string;
65+
profile: Profile;
7166
}
72-
export interface League {
73-
cities: {
74-
[k: string]: Team;
75-
};
67+
export interface Profile {
68+
username: string;
69+
age?: number;
70+
hobbies: string[];
7671
}
77-
export interface Team {
78-
name: string;
79-
sport: "football" | "basketball";
80-
athletes: Athlete[];
72+
```
73+
...and can be used in your typescript code with complete confidence.
74+
```ts
75+
import { LoginCredentials, LoginResponseData } from './apiTypes.ts';
76+
77+
async function login(
78+
credentials: LoginCredentials,
79+
resolve: (data: LoginResponseData) => void,
80+
reject: (error: string) => void,
81+
) {
82+
try {
83+
const response: Response = await fetch('/login/', {
84+
method: 'POST',
85+
headers: {'Content-Type': 'application/json'},
86+
body: JSON.stringify(credentials),
87+
});
88+
const data: LoginResponseData = await response.json();
89+
resolve(data);
90+
} catch (error) {
91+
reject(error.message);
92+
}
8193
}
8294
```

pydantic2ts/cli/script.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,28 @@
33
import importlib
44
import os
55
import shutil
6-
from pydantic import BaseModel, create_model
6+
from pydantic import BaseModel, Extra, create_model
77
from tempfile import mkdtemp
88
from typing import Type, Dict, Any, List
99
from types import ModuleType
1010

1111

12-
def remove_property_titles(schema: Dict[str, Any], model: Type[BaseModel]) -> None:
12+
def clean_schema(schema: Dict[str, Any], model: Type[BaseModel]) -> None:
1313
"""
14-
Monkey-patched method for removing titles from JSON schema properties.
15-
If we don't do this, each property will have its own interface in the
16-
resulting typescript file (which is a LOT of unnecessary noise).
14+
Monkey-patched method for cleaning up resulting JSON schemas by:
15+
16+
1) Removing titles from JSON schema properties.
17+
If we don't do this, each property will have its own interface in the
18+
resulting typescript file (which is a LOT of unnecessary noise).
19+
2) Setting 'additionalProperties' to False UNLESS Config.extra is explicitly
20+
set to "allow". This keeps the typescript interfaces clean (ie useful).
1721
"""
1822
for prop in schema.get('properties', {}).values():
1923
prop.pop('title', None)
2024

25+
if model.Config.extra != Extra.allow:
26+
schema['additionalProperties'] = False
27+
2128

2229
def not_private(obj) -> bool:
2330
"""Return true if an object does not seem to be private"""
@@ -95,10 +102,10 @@ def main(
95102
models = extract_pydantic_models(importlib.import_module(module))
96103

97104
for m in models:
98-
m.Config.schema_extra = staticmethod(remove_property_titles)
105+
m.Config.schema_extra = staticmethod(clean_schema)
99106

100107
master_model = create_model('_Master_', **{m.__name__: (m, ...) for m in models})
101-
master_model.Config.schema_extra = staticmethod(remove_property_titles)
108+
master_model.Config.schema_extra = staticmethod(clean_schema)
102109

103110
schema_dir = mkdtemp()
104111
schema_file_path = os.path.join(schema_dir, 'schema.json')

pydantic2ts/examples/api.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
from fastapi import FastAPI
2+
from pydantic import BaseModel
3+
from typing import List, Optional
4+
5+
api = FastAPI()
6+
7+
8+
class LoginCredentials(BaseModel):
9+
username: str
10+
password: str
11+
12+
13+
class Profile(BaseModel):
14+
username: str
15+
age: Optional[int]
16+
hobbies: List[str]
17+
18+
19+
class LoginResponseData(BaseModel):
20+
token: str
21+
profile: Profile
22+
23+
24+
@api.post('/login/', response_model=LoginResponseData)
25+
def login(body: LoginCredentials):
26+
profile = Profile(**body.dict(), age=72, hobbies=['cats'])
27+
return LoginResponseData(token='very-secure', profile=profile)

pydantic2ts/examples/apiTypes.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/* tslint:disable */
2+
/**
3+
/* This file was automatically generated from pydantic models by running pydantic2ts.
4+
/* Do not modify it by hand - just update the pydantic models and then re-run the script
5+
*/
6+
7+
export interface LoginCredentials {
8+
username: string;
9+
password: string;
10+
}
11+
export interface LoginResponseData {
12+
token: string;
13+
profile: Profile;
14+
}
15+
export interface Profile {
16+
username: string;
17+
age?: number;
18+
hobbies: string[];
19+
}

pydantic2ts/examples/convert.sh

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,2 @@
11
#!/bin/bash
2-
3-
pydantic2ts --module pydantic2ts.examples.pydantic_models --output output.ts
2+
pydantic2ts --module pydantic2ts.examples.api --output apiTypes.ts

pydantic2ts/examples/output.ts

Lines changed: 0 additions & 21 deletions
This file was deleted.

pydantic2ts/examples/pydantic_models.py

Lines changed: 0 additions & 31 deletions
This file was deleted.

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def readme():
2020

2121
setup(
2222
name='pydantic-to-typescript',
23-
version='1.0.4',
23+
version='1.0.5',
2424
description='Convert pydantic models to typescript interfaces',
2525
license='MIT',
2626
long_description=readme(),

0 commit comments

Comments
 (0)