-
Notifications
You must be signed in to change notification settings - Fork 79
[QB] Optimize scalar cast maps #1268
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
[QB] Optimize scalar cast maps #1268
Conversation
Having scalars castable into others is the edge case. Optimize this type by checking if the two types are equal before checking the edge cases. This saves having to declare mapping for 80% of the scalars that can't be cast into another.
4c9de16 to
49f975e
Compare
|
|
||
| const returnTypes = new Set<string>(); | ||
|
|
||
| const uncastables = new Set( |
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.
Like uncrustables but saltier.
Open to different names.
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.
Nope, this is the perfect name 👨🍳
|
|
||
| const outerCastableTo = casting(outer.id); | ||
| staticMap.writeln([t` A extends ${getRef(outer.name)} ?`]); | ||
| if (!uncastables.has(outer)) { |
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.
All of these ifs feel dirty, but I understand wanting to keep the runtime logic next to compile time. But I'm not sure now if we should deviate since the compile time is changing its logic somewhat.
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.
Oh, I'm perfectly happy to make this division more clear here vs in the generated code: we should prioritize the maintenance of this code over the understandability of the generated code. No change needed, but I'm 100% with you on wanting to branch harder here instead of littering if statements everywhere.
|
This is to prevent an excessive depth error, and it appears that the current TS benchmarks do not measure these depth calculations. |
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.
Pull Request Overview
This PR optimizes the getSharedParentScalar type generation for the common "80%" case by short-circuiting comparisons when the two scalar types are equal and by excluding uncastable scalars.
- Moves the equality check (IsEqual) to the top of the generated type definition.
- Introduces an uncastables set to conditionally generate type branches and reduces the overall branch count.
|
Just tried v6 on my codebase again with this. This does fix the depth problem with this type alias, but |
scotttrinh
left a comment
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.
👑
|
|
||
| const returnTypes = new Set<string>(); | ||
|
|
||
| const uncastables = new Set( |
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.
Nope, this is the perfect name 👨🍳
|
|
||
| const outerCastableTo = casting(outer.id); | ||
| staticMap.writeln([t` A extends ${getRef(outer.name)} ?`]); | ||
| if (!uncastables.has(outer)) { |
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.
Oh, I'm perfectly happy to make this division more clear here vs in the generated code: we should prioritize the maintenance of this code over the understandability of the generated code. No change needed, but I'm 100% with you on wanting to branch harder here instead of littering if statements everywhere.
|
Thanks for the review @scotttrinh. export type scalarCastableFrom<T extends $.ScalarType> =
T extends _Project.$Status ? _Project.$Status :
...;
export type $Status = {
"InDevelopment": $.$expr_Literal<$Status>;
"Active": $.$expr_Literal<$Status>;
"Terminated": $.$expr_Literal<$Status>;
"Completed": $.$expr_Literal<$Status>;
"DidNotDevelop": $.$expr_Literal<$Status>;
} & $.EnumType<"Project::Status", ["InDevelopment", "Active", "Terminated", "Completed", "DidNotDevelop"]>;What do you think about only checking the FQNs instead? export type scalarCastableFrom<T extends $.ScalarType> =
T extends $.ScalarType<infer N> ?
N extends "Project::Status" ? _Project.$Status :
...
: never;Actually following this train of thought we could just use a mapping instead. |
This is `O(1)` compared to the previous `O(N)`. This is easier on TS esp for enums, as enums have all of their members declared statically and the previous impl was having TS check all of these members for duck compatibility.
|
Alright well if this truly is the direction, I should probably change |
0a34451 to
dbddbfa
Compare
dbddbfa to
50d5652
Compare
|
Ok I'm done here. These changes do fix all related problems in my codebase, blocking my v6 upgrade. |
Fixes #1242.
scalarCastableFrom/scalarAssignableByThis changes the
scalarCastableFrom/scalarAssignableBytypes to just pick the type based on the given type's FQN.This is much easier for TS, esp with generated enums, as their members are statically written as well and TS was duck checking all of these.
getSharedParentScalarThis also changes
getSharedParentScalarto compare against the types' FQN.This should be much more performant and still safe since these are scalar types with unique names.
We can also skip enumerating every case if
A&Bare the same.This saves having to declare mapping for 80% of the scalars that can't be cast into another.
This removes 79 branches with my generated client, leaving only 6 specific scalar branches.
Before:
After: