tkrajina / typescriptify-golang-structs Goto Github PK
View Code? Open in Web Editor NEWA Golang struct to TypeScript class/interface converter
License: Apache License 2.0
A Golang struct to TypeScript class/interface converter
License: Apache License 2.0
Disclaimer: The following has only been tested using WithInterface(true)
- as we're only using interfaces.
Pointers behave as if ,omitempty
is applied to the field tag.
This example:
type MyDTO struct {
Example *float64 `json:"example"`
}
Outputs the following interface:
export interface MyDTO {
example?: number;
}
This would be correct if the field tag was appended with ,omitempty
, like so:
type MyDTO struct {
Example *float64 `json:"example,omitempty"`
}
But without ,omitempty
, the output should be:
export interface MyDTO {
example: number | null;
}
Are there anyway to achieve this?
Also if I've misunderstood anything (I'm very new to GO), I'd like to apologize in advance ๐ฌ
User
type User struct {
makeless_go_model.User
UserRoleId *iota.UserRole `gorm:"not null,index" json:"userRoleId"`
UserRole *UserRole `json:"userRole"`
}
/* Do not change, this code is generated from Golang structs */
export class UserRole {
id?: number;
name?: string;
static createFrom(source: any = {}) {
return new UserRole(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.name = source["name"];
}
}
export class Token {
id: number;
createdAt: Time;
updatedAt: Time;
deletedAt?: DeletedAt;
token?: string;
note?: string;
userId?: number;
user?: User;
teamId?: number;
team?: Team;
static createFrom(source: any = {}) {
return new Token(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.createdAt = this.convertValues(source["createdAt"], Time);
this.updatedAt = this.convertValues(source["updatedAt"], Time);
this.deletedAt = this.convertValues(source["deletedAt"], DeletedAt);
this.token = source["token"];
this.note = source["note"];
this.userId = source["userId"];
this.user = this.convertValues(source["user"], User);
this.teamId = source["teamId"];
this.team = this.convertValues(source["team"], Team);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class TeamInvitation {
id: number;
createdAt: Time;
updatedAt: Time;
deletedAt?: DeletedAt;
teamId?: number;
team?: Team;
teamUserId?: number;
teamUser?: TeamUser;
email?: string;
expire?: Time;
accepted?: boolean;
static createFrom(source: any = {}) {
return new TeamInvitation(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.createdAt = this.convertValues(source["createdAt"], Time);
this.updatedAt = this.convertValues(source["updatedAt"], Time);
this.deletedAt = this.convertValues(source["deletedAt"], DeletedAt);
this.teamId = source["teamId"];
this.team = this.convertValues(source["team"], Team);
this.teamUserId = source["teamUserId"];
this.teamUser = this.convertValues(source["teamUser"], TeamUser);
this.email = source["email"];
this.expire = this.convertValues(source["expire"], Time);
this.accepted = source["accepted"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class Team {
id: number;
createdAt: Time;
updatedAt: Time;
deletedAt?: DeletedAt;
name?: string;
userId?: number;
user?: User;
teamUsers: TeamUser[];
teamInvitations: TeamInvitation[];
tokens: Token[];
static createFrom(source: any = {}) {
return new Team(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.createdAt = this.convertValues(source["createdAt"], Time);
this.updatedAt = this.convertValues(source["updatedAt"], Time);
this.deletedAt = this.convertValues(source["deletedAt"], DeletedAt);
this.name = source["name"];
this.userId = source["userId"];
this.user = this.convertValues(source["user"], User);
this.teamUsers = this.convertValues(source["teamUsers"], TeamUser);
this.teamInvitations = this.convertValues(source["teamInvitations"], TeamInvitation);
this.tokens = this.convertValues(source["tokens"], Token);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class TeamUser {
id: number;
createdAt: Time;
updatedAt: Time;
deletedAt?: DeletedAt;
teamId?: number;
team?: Team;
userId?: number;
user?: User;
role?: string;
teamInvitations: TeamInvitation[];
static createFrom(source: any = {}) {
return new TeamUser(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.createdAt = this.convertValues(source["createdAt"], Time);
this.updatedAt = this.convertValues(source["updatedAt"], Time);
this.deletedAt = this.convertValues(source["deletedAt"], DeletedAt);
this.teamId = source["teamId"];
this.team = this.convertValues(source["team"], Team);
this.userId = source["userId"];
this.user = this.convertValues(source["user"], User);
this.role = source["role"];
this.teamInvitations = this.convertValues(source["teamInvitations"], TeamInvitation);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class User {
id: number;
createdAt: Time;
updatedAt: Time;
deletedAt?: DeletedAt;
name?: string;
email?: string;
emailVerification?: EmailVerification;
teamUsers: TeamUser[];
tokens: Token[];
static createFrom(source: any = {}) {
return new User(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.createdAt = this.convertValues(source["createdAt"], Time);
this.updatedAt = this.convertValues(source["updatedAt"], Time);
this.deletedAt = this.convertValues(source["deletedAt"], DeletedAt);
this.name = source["name"];
this.email = source["email"];
this.emailVerification = this.convertValues(source["emailVerification"], EmailVerification);
this.teamUsers = this.convertValues(source["teamUsers"], TeamUser);
this.tokens = this.convertValues(source["tokens"], Token);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class EmailVerification {
id: number;
createdAt: Time;
updatedAt: Time;
deletedAt?: DeletedAt;
verified?: boolean;
userId?: number;
user?: User;
static createFrom(source: any = {}) {
return new EmailVerification(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.createdAt = this.convertValues(source["createdAt"], Time);
this.updatedAt = this.convertValues(source["updatedAt"], Time);
this.deletedAt = this.convertValues(source["deletedAt"], DeletedAt);
this.verified = source["verified"];
this.userId = source["userId"];
this.user = this.convertValues(source["user"], User);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
export class DeletedAt {
static createFrom(source: any = {}) {
return new DeletedAt(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
}
}
export class Time {
static createFrom(source: any = {}) {
return new Time(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
}
}
export class User {
id: number;
createdAt: Time;
updatedAt: Time;
deletedAt?: DeletedAt;
name?: string;
email?: string;
emailVerification?: EmailVerification;
teamUsers: TeamUser[];
tokens: Token[];
userRoleId?: number;
userRole?: UserRole;
static createFrom(source: any = {}) {
return new User(source);
}
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.createdAt = this.convertValues(source["createdAt"], Time);
this.updatedAt = this.convertValues(source["updatedAt"], Time);
this.deletedAt = this.convertValues(source["deletedAt"], DeletedAt);
this.name = source["name"];
this.email = source["email"];
this.emailVerification = this.convertValues(source["emailVerification"], EmailVerification);
this.teamUsers = this.convertValues(source["teamUsers"], TeamUser);
this.tokens = this.convertValues(source["tokens"], Token);
this.userRoleId = source["userRoleId"];
this.userRole = this.convertValues(source["userRole"], UserRole);
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a.slice) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
I would like if the following struct:
// Header Item
type HeaderItem struct {
//Header name
Name string `json:"name"`
//Header value
Value string `json:"value"`
}
would convert to the following interface
/**
* Header Item
*/
interface HeaderItem {
/**
* Header name
*/
name: string;
/**
* Header value
*/
value: string;
}
Is it possible? Other option would be to have a special tag:
type HeaderItem struct {
Name string `json:"name" doc:"Header name"`
Value string `json:"value" doc:"Header value"`
_ struct{} `doc:"Header Item"`
}
tsscriptify cli doesn't have interface flag.
In Go I have the following struct
type Token struct {
*jwt.Payload // https://godoc.org/github.com/gbrlsnchs/jwt#Payload
User string `json:"user"`
Subcripcion string `json:"subcripcion"`
Rules map[string][]string `json:"rules" ts_type:"Record<string, Array<string>>"`
}
When you marshal that struct into JSON, the fields on *jwt.Payload
get marshalled too if they have any data at all, however, typescriptify ignores it due to lack of the JSON tag, if you add the tag, the fields are included as part of another type, like so:
export interface Payload {
iss?: string;
sub?: string;
aud?: string[];
exp?: Time;
nbf?: Time;
iat?: Time;
jti?: string;
}
export interface Token {
payload?: Payload;
user: string;
subcripcion: string;
rules: Record<string, Array<string>>;
}
I believe we should imitate how the fields end after a marshalling and output this result instead:
export interface Token {
iss?: string;
sub?: string;
aud?: string[];
exp?: Time;
nbf?: Time;
iat?: Time;
jti?: string;
user: string;
subcripcion: string;
rules: Record<string, Array<string>>;
}
Hello, I've forked the repo and extended it so that YAML tags are also supported, alongside existing JSON support, of course. I'm not sure whether this is something the project could use, but I can make a PR if you guys want.
Here's the commit.
To use this as a library in my module I had to do this
go get github.com/tkrajina/typescriptify-golang-structs/typescriptify
Just doing this
go get github.com/tkrajina/typescriptify-golang-structs
Results in an error
module found... but does not contain package
For JSII compatibility it would be handy to have a flag that enforces camelCase for methods and properties.
Currently, you need to explicitly add tags like ts_type:"Date"
and ts_transform:"new Date(__VALUE__)"
in order for the conversion to use the correct Typescript type Date
for time.Time
Go type. But in regard to third-party structs the developer cannot do this tag addition.
Thus, the library should handle this particular use case automatically.
Also, providing an interface that enables developers to handle these edge cases with third-party structs would be useful, like an .Add()
method with additional optional arguments to control ts_type
and ts_transform
.
I'm not really go-person.
I'm not sure how to install this tool
Move everything handled in createFrom()
to constructor()
(with optional params)
Field map[string]CustomStruct `json:"field"`
Should be converted to:
field: {[key: string]: CustomStruct};
At the moment, the way to convert maps is with custom types:
type Data struct {
Counters map[string]int `json:"counters" ts_type:"{[key: string]: number}"`
}
type test1 struct {
A int json:"a"
}
type test2 struct {
A [][]*test1 json:"a"
}
Won't work. Getting error:
Converting type main.test2
goroutine 1 [running]:
main.main()
../.../......./main.go:25 +0x114
exit status 2
If you change test2.A to 1d array, then works. So multi dimensional array of pointers doesn't seem to work for me
Could this compiler be extended to translate Golang functions as well as Golang structs to TypeScript?
For example, this Go function could be easily translated to TypeScript:
func add(x f64, y f64) int {
return x + y
}
This would be the equivalent function in TypeScript:
export function add(x:number,y:number):number{
return x+y;
}
It is possible to translate a subset of Go to TypeScript in this way, and there are several other features of Go that could be easily translated:
for
loops would become while
loops,go version go1.17 darwin/amd64
tscriptify -package=. -target=/Users/lucasloffel/js/ranked/ranked-ui/src/model/test.ts ./test.go
Parsing: ./test.go
go run /var/folders/15/n8y3ck4x6nxb93ymt3gtqr_m0000gn/T/3451915346/typescriptify_722417000.go
package command-line-arguments
imports .
imports .: import cycle not allowed
../../../../pkg/mod/github.com/tkrajina/[email protected]/typescriptify/typescriptify.go:12:2: missing go.sum entry for module providing package github.com/tkrajina/go-reflector/reflector (imported by github.com/tkrajina/typescriptify-golang-structs/typescriptify); to add:
go get github.com/tkrajina/typescriptify-golang-structs/[email protected]
panic: exit status 1
goroutine 1 [running]:
main.handleErr(...)
/Users/lucasloffel/go/pkg/mod/github.com/tkrajina/[email protected]/tscriptify/main.go:172
main.main()
/Users/lucasloffel/go/pkg/mod/github.com/tkrajina/[email protected]/tscriptify/main.go:129 +0x9c6
make: *** [ts-ranked-ui-model] Error 2
test.go
package model
type Test struct {
Name string `json:"peter"`
}
Custom imports specified with "-import" no longer get included in the generated output.
It looks like this happened when the flag processing got changed to directly use the Params struct in 5fd4c3a
Hi,
Given a struct:
type DeviceListing struct {
Identifier string `json:"identifier"`
AccountIdentifier string `json:"accountIdentifier"`
ContactEmail string `json:"contactEmail"`
}
I create two other alias structs:
type CreateListingResponse DeviceListing
type GetDeviceListingsResponse []DeviceListing
The CreateListingResponse Typescript interface is created correctly; however, for GetDeviceListingsResponse it just outputs:
export interface GetDeviceListingsResponse {
}
When I have vendoring enabled:
/tmp/1378569560/typescriptify_967979130.go:7:2: cannot find package "." in:
without it works fine
Custom option for Date type
...without having to add ts_type:"any"
Add a + in front of the assignment to coerce a string to a number, unless struct tag "json:",string"" present to avoid issues when sending to the golang json parser
static createFrom(source:any){
...
result.age=+source["age"]
^
}
I would love a way to specify that I don't want to limit myself to tagged fields. Additionally, fields such as json:",omitempty"
shouldn't be ignored.
I have a struct which have a field pointer:
type Test struct {
Field *string
}
However, the resulting conversion appears as follows:
export interface Test {
field: string
}
Ideally, we should have something like field?: string
instead.
Hey! Firstly, thank you so much for this project, it is incredibly helpful! :)
When a struct has an implicitly declared struct within it, Typescriptify doesn't know how to handle it and ends up returning nothing.
The struct in question:
type DashboardMetrics struct {
BusinessDailyTotal struct {
Monday float64 `json:"monday" yaml:"monday"`
Tuesday float64 `json:"tuesday" yaml:"tuesday"`
Wednesday float64 `json:"wednesday" yaml:"wednesday"`
Thursday float64 `json:"thursday" yaml:"thursday"`
Friday float64 `json:"friday" yaml:"friday"`
} `json:"business_daily_total" yaml:"business_daily_total"`
BusinessWeeklyTotal float64 `json:"business_weekly_total" yaml:"business_weekly_total"`
}
The generated Typescript:
export class DashboardMetrics {
business_daily_total: ;
business_weekly_total: number;
constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.business_daily_total = this.convertValues(source["business_daily_total"], );
this.business_weekly_total = source["business_weekly_total"];
}
convertValues(a: any, classs: any, asMap: boolean = false): any {
if (!a) {
return a;
}
if (a instanceof Array) {
return (a as any[]).map(elem => this.convertValues(elem, classs));
} else if ("object" === typeof a) {
if (asMap) {
for (const key of Object.keys(a)) {
a[key] = new classs(a[key]);
}
return a;
}
return new classs(a);
}
return a;
}
}
Declaring the struct as its own struct first would of course circumvent this, but it'd be great to see some sort of fix for this :) Apologies if this has already been posted as an issue, I couldn't see anything obvious when I did a quick skim!
If you use a prefix, the prefix is prepended to map keys, including primitives.
type Example struct {
Variable map[string]string `json:"variable"`
}
func main() {
typescriptify.New().WithPrefix("Testing").Add(Example{}).ConvertToFile("./output.ts")
}
Will generate something like this:
export class TestingExample {
variable: {[key: Testingstring]: string};
}
It also affects all subtypes not specified. This could be desirable behavior but it may cause issues if the types are later or previously defined elsewhere.
Need to add implementation and test case for []*Address.
How are we supose to convert to interfaces instead of classes? Using the code in the readme
converter := typescriptify.New().
Add(Person{}).
Add(Dummy{})
err := converter.ConvertToFile("ts/models.ts")
if err != nil {
panic(err.Error())
}
Custom option to create classes or interfaces
The README states that This is how now time.Time is managed in the library by default.
which was true as of commit c68aad5:
// manage time.Time automatically
result = result.ManageType(time.Time{}, "Date", "new Date(__VALUE__)")
However as of commit a7a821d this seems to be missing.
Can this functionality be restored or should the README be corrected?
package main
import (
"fmt"
"github.com/ranked-de/ranked-model"
"github.com/tkrajina/typescriptify-golang-structs/typescriptify"
)
func main() {
t := typescriptify.New()
t.CreateInterface = false
t.BackupDir=""
t.Add(ranked-model.Test{}) <----
err := t.ConvertToFile("/Users/lucasloffel/js/ranked/ranked-ui/src/model/test.ts")
if err != nil {
panic(err.Error())
}
fmt.Println("OK")
}
so I get
'd the new version of this package and generate a few more types for my project, but for some reasons empty interfaces like Time
and Decimal
(that one was new) were getting generated despite having ts_type
set on some fields of some structs and also using the new feature you recently merged and polished.
This is the generated file:
/* Do not change, this code is generated from Golang structs */
import { Decimal } from 'decimal.js'
export interface UpdateInvoicesRequest {
claves: string[];
estado: number;
}
export interface InformacionReferencia {
tipo_doc: number;
numero: string;
fecha_emision: Date;
codigo: number;
razon: string;
}
export interface OtroCargo {
tipo_documento: number;
detalle: string;
porcentaje: Decimal;
monto_cargo: Decimal;
}
export interface Exoneracion {
tipo_documento: number;
numero_documento: string;
nombre_institucion: string;
fecha_emision: Date;
porcentaje_exoneracion: Decimal;
monto_exoneracion: Decimal;
}
export interface Impuesto {
codigo: number;
codigo_tarifa: number;
tarifa: Decimal;
factor_iva?: Decimal;
monto: Decimal;
exoneracion?: Exoneracion;
}
export interface CodigoComercial {
tipo: number;
codigo: string;
}
export interface LineaDetalle {
numero_linea: number;
partida_arancelaria?: string;
codigos_comerciales: CodigoComercial[];
codigo_producto: string;
cantidad: Decimal;
unidad_medida: string;
unidad_medida_comercial?: string;
detalle: string;
impuesto: Impuesto[];
impuesto_neto: Decimal;
precio_unitario: Decimal;
monto_descuento?: Decimal;
naturaleza_descuento?: string;
monto_total: Decimal;
sub_total: Decimal;
base_imponible?: Decimal;
monto_total_linea: Decimal;
}
export interface Receptor {
nombre: string;
nombre_comercial: string;
identificacion_tipo: number;
identificacion_numero: string;
identificacion_extranjero: string;
provincia: number;
canton: number;
distrito: number;
barrio: number;
otras_senas: string;
otras_senas_extranjero: string;
correo_electronico: string;
telefono_codigo: number;
telefono_numero: string;
fax_codigo: number;
fax_numero: string;
}
export interface InvoiceCreateRequest {
emisor: string;
email: string;
tipo: number;
codigo_moneda: string;
tipo_cambio: Decimal;
actividad_economica: string;
condicion_venta: number;
plazo_credito: number;
caja: number;
sucursal: number;
receptor?: Receptor;
linea_detalle: LineaDetalle[];
otros_cargos: OtroCargo[];
informacion_referencia: InformacionReferencia[];
}
export interface UpdatePasswordRequest {
clave: string;
token: string;
}
export interface RefreshTokenRequest {
token: string;
}
export interface CredentialsRequest {
cuenta: string;
clave: string;
}
export interface PasswordResetRequest {
cuenta: string;
}
export interface ForwardInvoiceTo {
email: string;
}
export interface Consecutivo {
id: number;
tipo: number;
sucursal: number;
caja: number;
contador: number;
}
export interface GetCountersResp {
consecutivos: Consecutivo[];
cantidad: number;
}
export interface Emisor {
nombre: string;
nombre_comercial: string;
identificacion_tipo: number;
identificacion_numero: string;
provincia: number;
canton: number;
distrito: number;
barrio: number;
otras_senas: string;
correo_electronico: string;
telefono_codigo: number;
telefono_numero: string;
fax_codigo: number;
fax_numero: string;
}
export interface SearchIssuersResp {
issuers: Emisor[];
}
export interface UpdateCounterReq {
emisor: string;
tipo: number;
sucursal: number;
caja: number;
top: number;
}
export interface UpdateIssuerInfoReq {
nombre?: string;
nombre_comercial?: string;
id_tipo?: number;
id_num?: string;
correo?: string;
telf_codigo?: number;
telf_num?: string;
fax_codigo?: number;
fax_num?: string;
}
export interface UpdateIssuerLocationReq {
otras_senas?: string;
provincia?: number;
canton?: number;
distrito?: number;
barrio?: number;
}
export interface CreateSubscriptionReq {
identificacion: string;
identificacion_tipo: number;
tipo: number;
nombre: string;
correo: string;
primer_usuario_correo: string;
}
export interface SubscriptionIsDueResponse {
yes: boolean;
}
export interface Decimal { // HERE!!
}
export interface Subscription {
tipo: number;
identificacion_tipo: number;
identificacion_numero: string;
nombre: string;
email: string;
primer_pago?: Date;
siguiente_pago?: Date;
fecha_creado: Date;
fecha_actualizado: Date;
costo: Decimal;
adicional: Decimal;
}
export interface ListSubscriptionsResponse {
suscripciones: Subscription[];
cantidad: number;
}
export interface UpdateSubscriptionPrice {
base?: Decimal;
adicional?: Decimal;
}
export interface UpdateSubscriptionStatus {
emisores: string[];
estado: number;
}
export interface Time { // HERE TOO!
}
export interface Token {
iss?: string;
sub?: string;
aud?: string[];
exp?: Time;
nbf?: Time;
iat?: Time;
jti?: string;
user: string;
subcripcion: string;
rules: {[key: string]: string[]};
}
export interface Resumen {
total_servicios_gravados: Decimal;
total_servicios_exentos: Decimal;
total_servicios_exonerado: Decimal;
total_mercancias_gravadas: Decimal;
total_mercancias_exentas: Decimal;
total_mercancias_exonerada: Decimal;
total_gravado: Decimal;
total_exento: Decimal;
total_exonerado: Decimal;
total_venta: Decimal;
total_descuentos: Decimal;
total_venta_neta: Decimal;
total_impuesto: Decimal;
total_iva_devuelto: Decimal;
total_otros_cargos: Decimal;
total_comprobante: Decimal;
}
export interface FormaPago {
nombre: string;
codigo: number;
}
export interface Documento {
id: number;
clave: string;
consecutivo: string;
tipo: number;
caja: number;
sucursal: number;
estado: number;
emisor: Emisor;
receptor?: Receptor;
actividad: string;
fecha_emision: Date;
condicion_venta: number;
plazo_credito: number;
forma_pago: FormaPago[];
linea_detalle: LineaDetalle[];
otros_cargos: OtroCargo[];
informacion_referencia: InformacionReferencia[];
codigo_moneda: string;
tipo_cambio: Decimal;
normativa: string;
resumenes?: Resumen;
}
export interface Tokens {
access: string;
refresh: string;
}
and this is the Golang file:
package main
import (
"flag"
"os"
"path/filepath"
"time"
"github.com/sirupsen/logrus"
"github.com/tkrajina/typescriptify-golang-structs/typescriptify"
http_transport "gitlab.com/kuecr/fero/backend/monolith/pkg/http"
"gitlab.com/kuecr/fero/backend/monolith/pkg/json_web_token"
"gitlab.com/kuecr/fero/backend/monolith/pkg/service"
)
func main() {
ubicacionPtr := flag.String("salida", "", "archivo ts donde iran los tipos generados")
flag.Parse()
archivo, err := filepath.Abs(*ubicacionPtr)
if err != nil {
logrus.Panicf("no se pudo obtener ubicacion absoluta: %v", err)
}
converter := typescriptify.New()
// convierte time.Time en Date para Typescript
converter = converter.ManageType(time.Time{}, typescriptify.TypeOptions{TSType: "Date"})
converter.Add(http_transport.UpdateInvoicesRequest{})
converter.Add(http_transport.InvoiceCreateRequest{})
converter.Add(http_transport.UpdatePasswordRequest{})
converter.Add(http_transport.RefreshTokenRequest{})
converter.Add(http_transport.CredentialsRequest{})
converter.Add(http_transport.PasswordResetRequest{})
converter.Add(http_transport.ForwardInvoiceTo{})
converter.Add(http_transport.GetCountersResp{})
converter.Add(http_transport.SearchIssuersResp{})
converter.Add(http_transport.UpdateCounterReq{})
converter.Add(http_transport.UpdateIssuerInfoReq{})
converter.Add(http_transport.UpdateIssuerLocationReq{})
converter.Add(http_transport.CreateSubscriptionReq{})
converter.Add(http_transport.SubscriptionIsDueResponse{})
converter.Add(http_transport.ListSubscriptionsResponse{})
converter.Add(http_transport.UpdateSubscriptionPrice{})
converter.Add(http_transport.UpdateSubscriptionStatus{})
converter.Add(json_web_token.Token{})
converter.Add(service.Documento{})
converter.Add(service.Tokens{})
converter.AddImport("import { Decimal } from 'decimal.js'")
converter.BackupDir = ""
converter.CreateInterface = true
err = converter.ConvertToFile(archivo)
if err != nil {
logrus.Errorf("no se pudo convertir structs a tipos typescript: %v", err)
os.Exit(1)
}
}
I want through each struct there and checked that each field with a decimal.Decimal
type has its ts_type:"Decimal"
tag set and converter = converter.ManageType(time.Time{}, typescriptify.TypeOptions{TSType: "Date"})
seems correct to me, but, yeah, despite that I get empty interfaces (and Decimal as an empty interface is a new error, I've never see that behavior before)
In my fork I tried to reproduce the error with what I thought was the culprid: https://github.com/shackra/typescriptify-golang-structs/blob/weird-bug/typescriptify/typescriptify_test.go#L444
But surprisingly the test runs fine:
โ go test ./... -run TestAnonymousStructWithManagedType
? _/home/jorge/code/typescriptify-golang-structs/example [no test files]
? _/home/jorge/code/typescriptify-golang-structs/tscriptify [no test files]
----------------------------------------------------------------------------------------------------
export interface Target {
my_time: Date;
price: Decimal;
}
----------------------------------------------------------------------------------------------------
OK: export interface Target {
OK: my_time: Date;
OK: price: Decimal;
OK: }
tmp ts: /tmp/728430900.ts
executing: /tmp/728430900.js
--- FAIL: TestAnonymousStructWithManagedType (1.30s)
typescriptify_test.go:342:
Error Trace: typescriptify_test.go:342
typescriptify_test.go:319
typescriptify_test.go:469
Error: Expected nil, but got: &exec.ExitError{ProcessState:(*os.ProcessState)(0xc00000e640), Stderr:[]uint8(nil)}
Test: TestAnonymousStructWithManagedType
Messages: ../../../../../tmp/728430900.ts(3,12): error TS2304: Cannot find name 'Decimal'.
FAIL
FAIL _/home/jorge/code/typescriptify-golang-structs/typescriptify 1.300s
FAIL
Need to add customTSType logic similar to AddSimpleField
Enums support
From tscriptify/main.go:78
packageParts := strings.Split(packagePath, string(os.PathSeparator))
packages in go always use forward slash even on windows
https://stackoverflow.com/questions/45323772/invalid-import-path-go-windows
should be
packageParts := strings.Split(packagePath, "/")
Hi,
So, I am using project to generate TypeScript structures for Protocol Buffers generated GO structures. It works.
But, I am using int64 in Prtocol Buffers, which correctly serialize as Strings, because JavaScript Number is not large enough for int64. So, the workaround is to serialize int64 to string.
That means that this library would have to adjust for this specific scenario to generate string instead of Number.
I have made a change to suite me in my fork, but I really would like to push it over time into this project. so, I am opening this issue to discuss the most desired solution.
Mine is pretty straightforward: kulak@5421143 Unfortunately, I have changed the package name to mine to be able to work with the package today. So, I cannot make Pull Request without cleanup.
Thank you
Would be nice if it was also possible the other way around, from Typescript to Golang struct.
Following the docs I am doing
type MyEnum string
const (
Val1 = MyEnum("val1")
Val2 = MyEnum("val2")
)
var AllMyEnums = []MyEnum{ Val1, Val2 }
func (l MyEnum) TSName() string {
return string(l)
}
and now, when I run
tscriptify -package=package/with/your/models -target=target_ts_file.ts path/to/file/with/structs.go
it only generates for the other structs, and ignores the enum
when i looked at the code that is being generated, and added the line t.AddEnum(m.AllMyEnums)
manually, it generated the ts file correctly.
Having some trouble? panic: cannot find type for ptr (addons/)
Hi - great project.
I noticed that there is a message that's being output to stderr and there's no way of disabling it because the logic test is always true:
https://github.com/tkrajina/typescriptify-golang-structs/blob/master/typescriptify/typescriptify.go#L131-L133
Happy to do a PR for this...
Add test case and implementation for "type User=Person"
It would be great if this tool could convert Go generics to Typescript generics
type SimpleTile struct {
AuditID string `json:"audit_id,omitempty"`
Image string `json:"image"`
}
type WideTile struct {
SimpleTile
Title string `json:"title"`
ButtonText string `json:"button_text"`
}
type DefaultTile struct {
SimpleTile
Title string `json:"title"`
ProviderName string `json:"provider_name"`
ProviderFavicon string `json:"provider_favicon"`
}
type Teaser[T DefaultTile | SimpleTile | WideTile] struct {
Tiles []T `json:"tiles"`
}
Hey trying to use this with my project, and it has largely been really helpful, but I noticed it panics when a struct value is an array with defined length.
For example this panics due to Filters
type ExampleStruct {
Active bool `json:"active"`
Filters [3]string `json:"filters"`
}
The above produces the following panic message
panic: Cannot find type for , fideld: vibe
goroutine 1 [running]:
main.main()
/path/to/my/repo/gen-ts-types.go:13 +0x1694
exit status 2
But if I remove the size requirement from the struct, it works without issue.
type ExampleStruct {
Active bool `json:"active"`
Filters []string `json:"filters"`
}
Even being able to just ignore this size requirement in the typescript types would be useful for me
Hello and thanks for this nice library.
Does this library support Enums somehow? Should the following be converted to a Typescript Enum https://www.typescriptlang.org/docs/handbook/enums.html? Please check bellow an example.
`type LeaveType string
const(
AnnualLeave LeaveType = "AnnualLeave"
Sick LeaveType = "Sick"
BankHoliday LeaveType = "BankHoliday"
Other LeaveType = "Other"
)`
I think it would be helpful to add a tag to certain structs or struct properties which would notify the tool to not convert the identified structs/properties
I have this issue case where I need to import Decimal
from decimal.js
because I'm sending currency values back and forth between the frontend and the backend, and I wouldn't want to send strings instead, so, if I pass ts_type:"Decimal"
I have this problem where the import does not happen anywhere in the file and the TS file is filled with linting errors and does not work at all.
Having a way to add custom imports would become handy to handle this sort of use cases.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.