Closed opensaucerer closed 1 year ago
Thanks a lot man! Perhaps will work together some time haha. And I don't exactly use the term 'backend developer' for myself; software engineer just works 😄 - I write whatever needs to be written and if i can't; well, I learn to.
package main
type Foo struct {
Name string
Age int
}
type Bar struct {
Foo
Age int
}
func main() {
foo := Foo{"foo", 10}
bar := Bar{foo, 20}
println(bar.Age) // 20
println(bar.Foo.Age) // 10
}
Here, what exactly is the real age?🤣 I could tweak it to always just merge the types the way you seem to suggest but it might be unpleasant. I get that the direct definition overrides the embedded one but imagine they're different types now and both now get combined into:
{
...
age: string;
age: number;
...
}
I apologize but I am honestly unsure how to go about this one in a way that it spits out accurate Typescript that will ALWAYS match whatever you do in code, if you know how to possibly resolve this in a way that allows for both cases, please make a PR, I'll gladly review and merge, I am out of ideas for this one.
Exactly...nothing like backend or frontend...just software.
I was going to mention generics in my issue but then, I was like..."that's another level of thought process".
I actually reason with your example above and I think the idea here is to focus on Typescript not Golang. When an interface or type extends another interface or type...any fields that are similar becomes superimposed by the other. Nevertheless, to allow it such that users are in control, you can factor this into a configuration such that one could say SuperimposeCollidingFields
. This even goes on to stress that a created client should be permissive such that I can update the config on the fly. I don't really think the idea of one client per file adds simplicity without a higher cost to efficiency...or maybe I'm missing something.
Whether you're chaining on to a client or creating a new one, it's going to do the same thing; return a pointer to a new gots instance, something you could easily do from anywhere in your code by just creating a new one, don't see the efficiency hit or boost, but I'll add that feat in a future release.
Hun? a new instance or the same instance? Wondering why it has to be a new instance? Maybe I'm missing something?
func New(config Config) *gots {
if config.OutputFile == "" {
config.OutputFile = "index.d.ts"
}
return &gots{
config,
}
}
func (g *gots) Reconfigure(config Config) error {
if config.OutputFile == "" {
config.OutputFile = "index.d.ts"
}
// to ensure the fields are dynamically handled, use reflect to scan through the fields an load them unto the g pointer that way you can still return the same instance...referencing the same space in memory.
return g
}
Do correct me if I'm wrong, surely.
The same instance just gives you the same exact configurations as the original instance, and modifying that instance just changes the configuration globally, what’s the point then?
I would advice you take a look at this to better understand: https://github.com/aosasona/gots/blob/fa3d9f02370589f124706fde784a145d7aad2551/file.go#L10
The output file is grabbed from the config itself no matter where or how many times you call register, changing the same instance just changes that for everything else.
func New(config Config) *gots { if config.OutputFile == "" { config.OutputFile = "index.d.ts" } return &gots{ config, } } func (g *gots) Reconfigure(config Config) error { if config.OutputFile == "" { config.OutputFile = "index.d.ts" } // to ensure the fields are dynamically handled, use reflect to scan through the fields an load them unto the g pointer that way you can still return the same instance...referencing the same space in memory. return g }
Then you’re just changing it globally? Still one output file, your pitch was multiple output files.
You're correct...what I was actually pointing at is the global change...wasn't referring to a local scope.
Like, you give me a gots client, I set the output file to something I need at this moment, I use it to call register and it compile one file and that's the end. I don't need to create another instance that'll need to be gc'ed when done.
I know my instance is still available to me but since i know I want a different output path, I just reconfigure it and use it again for the set of types I want...on and on for different output path but the same client.
and then I don't need the client anymore...I can call g.Close()
but this one might not be necessary.
I think it's the most idiomatic way I've seen go project clients be implemented in, if I'm not wrong.
say for example...I create a google cloud storage client that only operates on one bucket...and have to create another for another bucket.
don't know if you do get me, my lord.
Would suggest you go through the code again to understand how it works but then again, I have created an issue to branch off from an instance, will work on that in the future.
And it’s not something you use in production, I’d expect you keep it in one place, it’s like suddenly modifying your database instance in one file out of 100 when you could do it in whatever init function you created it in.
Would suggest you take a look at this package if you need something more fully fledged; https://github.com/tkrajina/typescriptify-golang-structs
Yeah...I saw the production advice in the readme...and I did read the code before even opening this issue.
Then when I tried the package...I wanted to use it in exporting another struct to another location. it was then I realized I needed to create a new client...then I thought to suggest...that if this is the first problem I face trying out a cool package, it might as well be something others might have thought of or faced.
So, I just checked the link you shared above and I saw this which is almost like what I'm mentioning if not the exact same.
converter := typescriptify.New().
Add(Person{}).
Add(Dummy{})
err := converter.ConvertToFile("ts/models.ts")
if err != nil {
panic(err.Error())
}
and, just pointing out that I'm not actually saying this should be done now or anything...just loved the idea of the project and some others I've seen you work on.
have you worked on building a web framework before in any language? I think you're still a student..I'm still a student too..lol...see me talking like I'm done with school...but I think you're outside the country....I don't know if your wizardry will be interested in the idea of this framework...it's not just any regular one...it's BARF - (Basically, A Remarkable Framework) and the name BARF is from marvel's reference, Binarily Augumented Retro Framing....you know it?
I also have an idea for a validation layer, a struct validation layer like that of Joi in JavaScript. I call the package Vibranium - probably the most versatile go validation package.
EDIT:
I just read this reply again and I realized there's absolutely no reference to why I asked if you're a student. I asked due to availability.
It appears I have confused you, my bad. Let me try to re-explain to you again.
First, I need you to consider what happens if you call ConvertToFile
in main.go and then proceed to do .Add() in another file. You might be missing the point here because of how I have explained things.
This .Add().Add() is the same as passing it all at once as I have done with Register which ensures: it does no work outside that one call, and nothing is left open as you implied. My mistake was not including a way to branch off from a global client and when I do that, you will be able to do this:
modelClient := helper.GotsClient.To("./somefile.ts") // assuming you have created a global client in the helper package called GotsClient
// then you can give that one a new config or let it inherit from the original one (which means it will respect your Enable toggle in one place (you dig?)
modelClient.SetEnable(...)
modelClient.SetUseTypesForObject(...)
// or do it at once
modelClient.SetOpts(gots.Config{...})
The problem with being able to call register anywhere and then another SaveToFile
method is that; it'll always be a problem, there is a very good chance that no matter where you call SaveToFile, there will be one or more unregistered struct. Without looking at the code, I can tell that the way this works is by appending each struct to a slice (I just make you pass in the structs directly) and working on that slice when you call the Convert thingy; which was my initial idea but you then have to worry about "Has all my types been registered yet?", "Where should I call Save to make sure they all get registered?" etc
Yeah, I have seen the Vibranium package you're working on, I follow you, so it showed up in my feed when you created the repo 🤣 - I have been waiting for the release, would also have to use that your multi-storage package for a project I am working on soon; expect issues to when I do 😈
A framework; yeah, thought about it for PHP but it's probably going to suck more than anything else already on the market since I won't really have the time to actually implement things well or maintain it. I just use Validator for validation and it hasn't pissed me off enough to warrant reinventing it
Oh...that's correct....Add() will simply append to a slice...that's the most reasonable way to do it without expending much memory but, of course, with some irrelevant drawbacks to gc not triggering even when you nil the array within the calling function (backing array ish).
I actually like the way you let your Register function take variadic inputs... I don't like the builder design pattern used by this other package... I referenced that snipped because I wanted to point your attention the .ConvertToFile()
method and from your last comment, it seems that's the same thing you picked up from the snippet.
Now, regarding your edit to your comment just now about worrying if all structs are registered...I believe this is where you can then make it such that well, the builder pattern comes in, in a way...really wish I can use emoji directly here...cos I'm just smiling seeing that the project is getting more involved and I don't think you want that...but, yeah...the builder pattern comes in-ish such that I can call Register more than one to append another set of structs before calling the .To()
method...because after .To()
gets called...every registered structs should get disposed of.
Essentially, gots might have to become more involved than you initially thought and you don't have to much it such, if not necessary but because I like learning and talking about exciting things with people, I just opted to talk about this one.
Yeah, I have seen the Vibranium package you're working on, I follow you, so it showed up in my feed when you created the repo rofl - I have been waiting for the release, would also have to use that your multi-storage package for a project I am working on soon; expect issues to when I do smiling_imp
A framework; yeah, thought about it for PHP but it's probably going to suck more than anything else already on the market since I won't really have the time to actually implement things well or maintain it. I just use Validator for validation and it hasn't pissed me off enough to warrant reinventing it
Looking forward to you using it and I'll be expecting issues...I love issues 😂 (had to copy this emoji and paste here...dang!)
Now that I'm sorta out of work...I'll take all the time to just build my ideas... all of them reside here https://abbrefy.xyz/projects
Exactly why I'm opting to do a framework in golang. one, I don't like frameworks just as much as I don't like inheritance...i think they end up making things complex...I see you use fiber...I'm not much of a fan of any of them...I just use gorilla mux to do stuff and write everything else myself.
But the idea of BARF is to make it such that you can build backend servers without having to actually create any instance of the app client itself. Like say in express const app = Express()
or in fibre app := Fiber.New()
...in barf, you don't need to create a client...just access everything directly from the package itself name itself barf.Get()
, barf.Router().Get()
etc. and when you want to expose the port...barf.Beck()
, for custom configurations, barf.Stark()
I've started and I'll push in a few days, my progress.
Then later on, barf will become such that REST is not supported by default rather you have to enable the support for rest in the configuration to barf.Stark()
.....by the way, Stark() as in Tony Stark and Beck() as in Quitine Beck 😂
There's something I like about you, it's that you don't write software like most people I've met...this means you don't do the boring and what I crudely call "shallow" programming other backend engineers engage in...just building servers/APIs (bro...can we just stop ffs...lol). Not saying it's bad or clowning down on anyone...rather just hoping we'll have more people engage in more exciting software stuff as opposed to spending time arguing over FE/BE narrative.
Now,
gots
is awesome and I like the naming you use for your creations as well...I think we have these parts in common and we might just end up writing software together...maybe, maybe not...but here's the problem statement for this issue.After a call to
gots.New()
the client returned can only be used once for a single file which means I have to create a new client for another file. I think one can make it such thatgots.New()
still takes the defaultOutputFile
parameter but allows the returned client to implement a function that allows for new output. Something likegots.New().Register().Output()
or a cooler name.I call them complex primitives because they are basic types in Golang that do not exist in Typescript as it inherits from JavaScript's lack of granular support for large integers. I say granular because we have
BigInt
.Consider the following...you can easily see that the out from
gots
will be a single call totoSingleType
which results in a Typescript error.gots
's readme makes use of some structs with fields extending other struct types but a modification as follows will produce not an error but an undesired result.Awesome package, once again.