Package: ts-type
ts_typepypi:https://pypi.org/project/ts-type/Overviewts_typeis a utility to generate typescript type definitions from python's objects.This library uses python's dynamic nature to collect and construct type definitions. You can pick any objects which you wan to export. For example, assume that you have web api which statically typed with typing module and you can retrieve those types and urls. Use the information to generate union of api types easily like below:Run this in REPL (python >= 3.9):fromdataclassesimportdataclassimportts_typeasts@dataclassclassRequestForApiA:a:intb:str@dataclassclassResponseForApiA:result:booldefapi_a(request:RequestForApiA)->ResponseForApiA:...@dataclassclassRequestForApiB:@dataclassclassItem:id:intname:stritems:list[Item]defapi_b(request:RequestForApiB)->int:...apis={'/api/a':api_a,'/api/b':api_b,}classApis(ts.NodeCompatibleClass[ts.NodeBuilder]):@classmethoddefconvert_to_node(cls,builder:ts.NodeBuilder)->ts.TypeNode:returnts.Union(of=[ts.Object(attrs=dict(# It must be `literal_to_node` because# `type_to_node` treats str as ForwardRef.url=builder.literal_to_node(url),# `type_to_node` supports many builtin types such as# dataclasses, Enums, Lists, Unions.request=builder.type_to_node(api.__annotations__['request']),response=builder.type_to_node(api.__annotations__['return']),))forurl,apiinapis.items()])if__name__=='__main__':ts.generator.add(Apis,'apis','Apis')ts.generator.generate('output_dir')output:exporttypeApis={"url":"/api/a";"request":__main____RequestForApiA;"response":__main____ResponseForApiA;}|{"url":"/api/b";"request":__main____RequestForApiB;"response":number;};type__main____RequestForApiA={"a":number;"b":string;};type__main____ResponseForApiA={"result":boolean;};type__main____RequestForApiB={"items":__main____RequestForApiB__Item[];};type__main____RequestForApiB__Item={"id":number;"name":string;};You can use it to extract request and response types from url.type RequestForApiA = Extract<Apis, {url: "/api/a"}>['request'];
type ResponseForApiA = Extract<Apis, {url: "/api/a"}>['request'];
type RequestForApiB = Extract<Apis, {url: "/api/b"}>['request'];
type ResponseForApiB = Extract<Apis, {url: "/api/b"}>['request'];Basic Usageadd types toTypeDefinitionGenerator. (you can usets_type.generator, which is an instance of the class)callTypeDefinitionGenerator.generatefromdataclassesimportdataclassfromtypingimportOptional,List,Dictimportts_typeasts@ts.gen_type@dataclassclassMyCustomType:a:intb:Optional[str]c:List[str]d:Dict[str,int]if__name__=='__main__':ts.generator.generate('output_dir')Support Custom TypesYou can customize builder to supports any objects. I use my own librarycleanedto validate requests. This is how I customize builder to support the library.classBuilder(ts.NodeBuilder):defhandle_unknown_type(self,t:Any)->ts.TypeNode:ifisinstance(t,type):ifissubclass(t,cl.Cleaned):returnself.define_cleaned(t)returnsuper().handle_unknown_type(t)defdefine_cleaned(self,t:Type[cl.Cleaned],exclude:set[str]=set())->ts.TypeNode:ret:ts.TypeNode=self.define_ref_node(t,lambda:ts.Object(attrs={k:self.type_to_node(self.field_to_type(f))fork,fint._meta.fields.items()},omissible={kfork,fint._meta.fields.items()iff.has_default}))if(exclude:=exclude&set(t._meta.fields)):ret=ts.Omit(ret,ts.Union([self.literal_to_node(k)forkinsorted(exclude)]))returnretdeffield_to_type(self,t:cl.Field)->Type:ifisinstance(t,cl.OptionalField):vt=self.field_to_type(t.field)returnUnion[None,cl.Undefined,vt]elifisinstance(t,cl.ListField):vt=self.field_to_type(t.value)returnlist[vt]elifisinstance(t,cl.SetField):vt=self.field_to_type(t.value)returnset[vt]elifisinstance(t,cl.DictField):kt=self.field_to_type(t.key)vt=self.field_to_type(t.value)returndict[kt,vt]elifisinstance(t,cl.NestedField):returnt._server()elifisinstance(t,cl.EnumField):returnt._server()else:returnts.resolve_typevar(t.__class__,cl.Field.__parameters__[0])# don't forget to pass the builder to generatorts.generator.generate(output_dir,builder_cls=Builder)Other Examplessave following code asexample.pyfromdataclassesimportdataclassfromdatetimeimportdatetimefromenumimportEnumfromtypingimportTypeVar,Generic,Optionalimportts_typeastsT=TypeVar('T')@ts.gen_type@dataclassclassArticle:classType(Enum):news='news'essay='essay'@dataclassclassAuthor:id:intname:strid:inttype:Typetitle:straurhot:Authorpublished_at:datetime@ts.gen_type@dataclassclassPagination(Generic[T]):items:list[T]next_cursor:Optional[str]@ts.gen_type@dataclassclassResponse:status:intarticles:Pagination[Article]run:importexampleexample.ts.generator.generate('output_dir')output:output_dir/example.gen.tsexporttypeArticle=example__Article;exporttypePagination<T>=example__Pagination<T>;exporttypeResponse=example__Response;typeexample__Article={"id":number;"type":example__Article__Type;"title":string;"aurhot":example__Article__Author;"published_at":string;};typeexample__Article__Type="news"|"essay";typeexample__Article__Author={"id":number;"name":string;};typeexample__Pagination<T>={"items":T[];"next_cursor":string|null;};typeexample__Response={"status":number;"articles":example__Pagination<example__Article>;};