-
Notifications
You must be signed in to change notification settings - Fork 937
chore: switch to guts for typescript types generation #15801
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
ae94a07
to
6305d44
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Go part is mostly what I reviewed, looks pretty simple since everything is in guts now, lgtm
# Future Ideas | ||
|
||
- Use a yaml config for overriding certain types | ||
Uses it's own `go.mod` to exclude goja deps from the main go.mod. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does this matter in practice?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding of go.mod isn't complete, but I did not want anyone importing coder/coder
to see goja
as an indirect dependency.
Doing it this way prevents goja
from ever appearing in any go.mod/go.sum related to coder/coder
. Given it's a JS runtime in Go, feels like a jank dep to include.
display_name: null, | ||
avatar_url: null, | ||
quota_allowance: null, | ||
}); | ||
}; | ||
|
||
removeMember = async (groupId: string, userId: string) => { | ||
return this.patchGroup(groupId, { | ||
name: "", | ||
display_name: "", | ||
add_users: [], | ||
remove_users: [userId], | ||
display_name: null, | ||
avatar_url: null, | ||
quota_allowance: null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should these use omitempty
instead of setting null
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I talked to @Parkreiner about this, and we have to do some overhauling of our types when it comes to omitempty
vs null
.
For requests, maybe omitempty
is easier to use, but null
forces the user to acknowledge the fields exist. This actually raised a group member bug.
This is very similar to what we did in our Golang linter for structs: #8403
For most objects returned by Golang, omitempty
is incorrect, as we usually have a *string
or some pointer to something. Go json marshals this as null
, and the previous typescript code would treat the type as string | undefined
, but at runtime the type is null
. So it was just incorrect, however the truthy value is consistent.
tl;dr: We've been using undefined
and null
incorrectly in our typescript types, and it's too much to fix in this PR. I just had to make some changes to make the typescript compiler happy, even though nothing has functionally changed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more. 8000
Yeah, I think the ?:
syntax has better ergonomics in React component props, but in all other cases, having the explicit null
case is better for making sure we don't have invalid/malformed requests
I'd rather we overhaul things so that we have to deal with explicit null cases for anything involving an API call
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there's a little bit of cleanup to do, but I think that this is perfectly fine. A lot of the comments I'm leaving are more for me to make sure I don't miss anything when I try to go in and fix things up later on
readonly dynamic: Fi 8000 elds<boolean, A, string, S>; | ||
readonly comparable: boolean; | ||
} | ||
export type Custom = string | boolean | number | number | string[] | (number | null); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is a huge deal, but the TypeScript output here looks a little bit jank. I'm guessing the (number | null)
is from a nullable int in Go, but the type number | (number | null)
collapses down to just number | null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 I can definitely unest these
readonly Slice: Readonly<Array<R>>; | ||
readonly TwoD: Readonly<Array<Readonly<Array<R>>>>; | ||
readonly Slice: readonly R[]; | ||
readonly TwoD: readonly R[][]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't remember if we talked about this being out of scope for this PR, but just in case, this 2D array isn't deeply readonly. It's a readonly array of mutable R
arrays. The fully safe type would have to be readonly (readonly R[])[]
(as ugly/clunky as it is)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea, I need to recursively apply it 😢
@@ -150,7 +152,8 @@ export async function verifyConfigFlagArray( | |||
); | |||
|
|||
// Verify array of options with simple dots | |||
for (const item of opt.value) { | |||
// biome-ignore lint/suspicious/noExplicitAny: opt.value is any | |||
for (const item of opt.value as any) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Kind of wish we could've at least written the expression as const item of opt.value as any[]
, to get some degree of LSP feedback. I don't think it's a huge deal, since this is a test file, but yeah, we'll definitely need to clean up our types later on
@@ -166,7 +169,8 @@ export async function verifyConfigFlagEntries( | |||
); | |||
|
|||
// Verify array of options with green marks. | |||
Object.entries(opt.value) | |||
// biome-ignore lint/suspicious/noExplicitAny: opt.value is any | |||
Object.entries(opt.value as any) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole expression is a bit janky. I'll see if I can't clean up some if it during the two work days we have during Christmas break
display_name: null, | ||
avatar_url: null, | ||
quota_allowance: null, | ||
}); | ||
}; | ||
|
||
removeMember = async (groupId: string, userId: string) => { | ||
return this.patchGroup(groupId, { | ||
name: "", | ||
display_name: "", | ||
add_users: [], | ||
remove_users: [userId], | ||
display_name: null, | ||
avatar_url: null, | ||
quota_allowance: null, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I think the ?:
syntax has better ergonomics in React component props, but in all other cases, having the explicit null
case is better for making sure we don't have invalid/malformed requests
I'd rather we overhaul things so that we have to deal with explicit null cases for anything involving an API call
@@ -85,7 +85,11 @@ export const DERPPage: FC = () => { | |||
<section> | |||
<SectionLabel>Regions</SectionLabel> | |||
<div css={{ display: "flex", flexWrap: "wrap", gap: 12 }}> | |||
{Object.values(regions) | |||
{Object.values(regions!) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not a huge problem, but this !
isn't doing anything. At least from the type level, regions
itself can't be nullish
What this does
Hopefully nothing. This moves to using guts for generating our typescript types. Our typescript generator has been made a package, with opinions removed.
Some changes
Explicit
omitempty
becauseundefined
andnull
are different. Now it must be explicit. Addedomitempty
to some types to be consistent with how we were using them, usednull
for others.Future work
Future PRs should fix when we use
omitempty
and pointers to distinguishnull
or?
in the typescript. The current code types seem arbitrary and do not distinguish between the two. This new type-gen is more accurate.In golang,
omitempty
will marshal the json as{}
, whilenil
will marshal as{"field":null}
. We should be explicit when to doomitempty
and when to leave it as<type> | null
. Sometimes the field existing, but being null is preferred. We should migrate tonull
when we always want the field to exist.I will not do this in this PR, but will make follow ups to fix certain areas over time.
Bug
Fixed if you remove a user from a group, it deletes the display name.