We can't rely on typegen to always be 100% perfect, for example when it can't determine nullability from complex queries or with json columns.
In these cases we want to be able to "help it out" and modify the generated type output.
We can't modify the generated types, as generated code shouldn't be touched. It would be overwritten in subsequent code-gen-runs.
We currently also can't modify the type assignment in the template literal, as it also gets overwritten on every run.
the planned solution
Instead of overwriting the whole type definition for a processed template literal, we'll preserve modifications like intersections.
The additional type intersection with QueryEnhancement will be left untouched on subsequent runs.
In short: We can increase specificity with intersection types.
This method is very explicit, easy to read and keeps the type definition where it belongs.
If a col is unknown, it will take the shape of any intersecting type.
If a col is or string | null in the extracted type, intersecting it with { col: string } the intersection result will be only string, which is exactly what we want.
If the enhanced type doesn’t intersect (i.e. { col: number }) the resulting type for col becomes never. This is great, because in case the underlying col type changes and new types are generated, the dev would be alerted to the incompatibility with his enhancements.
up for debate
There are two things I have a strong opinion on, but would like some feedback from other users:
Allow other type modifications like completely overwriting extracted types i.e. Omit<queries.TestTable, 'col'>
I don't think we should allow this, because this has the potential for silent errors. If a col type changes but is overwritten in place, the dev will not be alerted to this new query result type. It will pretend it's still the same (overwritten) type.
What we want to do is increase specificity if slonik/typegen is not sure enough, not completely change the returned type.
In case a dev wants to use types differing from the typegen's result, he should make slonik/typegen ignore the query. At least that's explicit.
Overwrite on initial run
When typegen runs for the first time on a query, which already has typedefs in there, i.e. sql<{col: string}> I would keep the current behaviour and completely overwrite it to sql<queries.TestTable>`...`, rather than keeping it and resulting to smth. like sql<queries.TestTable & {col: string}>`...`
The rationale here is that if you are running slonik/typegen on a query for the first time which has typedefs, then the reason for those defs to be there is probably because you previously didn't have slonik/typegen.
So keeping them doesn't make much sense.
the problem
We can't rely on typegen to always be 100% perfect, for example when it can't determine nullability from complex queries or with json columns. In these cases we want to be able to "help it out" and modify the generated type output.
We can't modify the generated types, as generated code shouldn't be touched. It would be overwritten in subsequent code-gen-runs.
We currently also can't modify the type assignment in the template literal, as it also gets overwritten on every run.
the planned solution
Instead of overwriting the whole type definition for a processed template literal, we'll preserve modifications like intersections.
Example:
after generation will become
this can then be manually modified to
The additional type intersection with
QueryEnhancement
will be left untouched on subsequent runs.In short: We can increase specificity with intersection types. This method is very explicit, easy to read and keeps the type definition where it belongs.
unknown
, it will take the shape of any intersecting type.string | null
in the extracted type, intersecting it with{ col: string }
the intersection result will be onlystring
, which is exactly what we want.{ col: number }
) the resulting type for col becomesnever
. This is great, because in case the underlying col type changes and new types are generated, the dev would be alerted to the incompatibility with his enhancements.up for debate
There are two things I have a strong opinion on, but would like some feedback from other users:
Omit<queries.TestTable, 'col'>
I don't think we should allow this, because this has the potential for silent errors. If a col type changes but is overwritten in place, the dev will not be alerted to this new query result type. It will pretend it's still the same (overwritten) type. What we want to do is increase specificity if slonik/typegen is not sure enough, not completely change the returned type. In case a dev wants to use types differing from the typegen's result, he should make slonik/typegen ignore the query. At least that's explicit.
When typegen runs for the first time on a query, which already has typedefs in there, i.e.
sql<{col: string}>
I would keep the current behaviour and completely overwrite it tosql<queries.TestTable>`...`
, rather than keeping it and resulting to smth. likesql<queries.TestTable & {col: string}>`...`
The rationale here is that if you are running slonik/typegen on a query for the first time which has typedefs, then the reason for those defs to be there is probably because you previously didn't have slonik/typegen. So keeping them doesn't make much sense.
Looking forward to your feedback!