Open colinhacks opened 2 years ago
For convenience this function can be variadic. The inputs will be validated and joined with ", ".
Probably it accepting an array of strings is better since I imagine sometimes such lists would be formed dynamically, and ident(list)
is better than ident.apply(null, list)
.
Probably it accepting an array of strings is better
With a variadic function you can use the spread operator to pass in an array of identifiers: ident(...list)
. (This can be documented.)
This would verify that the input is a valid identifier with a regex.
Pretty much any Unicode string is a valid identifier as long as you quote it. The only hard rule is that an ident cannot start with $
or @
.
The cast type will be inferred from the input data according to the following rules:
I think it's probably safe to add the rest of the types to this list, ie. datetime, local_date/time, duration and bytes, since we can identify them with instanceof
. The only exception would then be uuid
, and the user would just have manually add a cast.
I think it's probably safe to add the rest of the types to this list
Indeed good call!
I see draft PR #381 implements this functionality. See also discussion on edgedb/edgedb.
This may be premature, but is there a way to expose the result type of a tagged template query to typescript for autocompletion / typechecking purposes? E.g. using .name
on an entry of a select query that does not include a name
field should be a type error.
No, this template tag will not be schema-aware nor can it infer types. That's virtually impossible to implement with just template literals - you should use the query builder if you want inferred types and typechecking.
That's virtually impossible to implement with just template literals
Indeed, but it would be possible with a VSCode extension. It would introspect each query at dev-time and tell typescript what type that is
It would introspect each query at dev-time and tell typescript what type that is
I'm not sure how that would work exactly, but I've observed some existing solutions in the same space:
[^1]: At least until we get F#-style type providers
One option is a codegen tool that would watch the source for queries tagged with edgeql<TResult>`select ...`
and generate the correct generic TResult
type for that query in a separate generated file just before building with tsc. The benefit of this approach is that you can implement automatically generated types without any changes to the standard typescript tooling. The downside is that you have pushed that complexity into the build system at the next layer up, by requiring you to run a different external program after every change to make sure types are in sync with queries. Another downside is that even though they don't have to keep the result type in sync with the query manually, the user still has to uniquely name and import each result type which is 'somewhat' annoying.
GraphQL libraries have taken this path in the past [^2] and seem to have converged on graphql-code-generator which is a centralized codegen integration point that alternate generators can plug into|plugin to.
Out of graphql-code-generator
, a promising approach is the gql-tag-operations babel plugin (source) which is "a babel plugin for rewriting the gql usages to actual imports". This might be the 'nicest' design I've seen because it minimizes user input down to typing the query and the source is automatically transformed on build into a fully-typed reference to a type in a different generated file. We may need to discuss how acceptable it is to require users to use babel so they can import a babel plugin, or find some other acceptable way to integrate what is basically preprocessing/macros into the typescript build system.
I prefer Option 2 for edgeql if possible. I expect that automatically adding types to edgeql``
queries by transforming the source during build would be very nice UX. Failing that, Option 1 would still be good.
I don't mean to derail this issue, and I think it would still be reasonable to implement the tagged template as described in the original post, and splitting this topic into a second issue. I also think that being able to generate typesafe code by simply tagging edgeql queries could be a big benefit and a reason why the feature requested by this issue should be implemented as a first step.
Is this still desirable given the queries
generator gives us something pretty close to this experience without needing to jump through a bunch of tooling hoops to get good type information? If the desired outcome was just sanitized inputs rather than co-location, feels like the queries
generator fits the bill.
Proposal: provide a template tag for writing sanitized EdgeQL queries.
User input
We'd overload the query methods to support an object-based query format:
{query: string; parameters: {[k:string]: any}}
The cast type will be inferred from the input data according to the following rules:
Array.isArray(data) === true
-> check that contents are homogenous primitives ->array<element-type>
object
->json
string
->str
number
->float64
bigint
->bigint
boolean
->bool
Implicit/assignment casting makes this workable. e.g. Assigning a
float64
to aint16
value will work without an explicit cast.Identifiers
There should be a separate API for specifying identifiers:
edgeql.ident()
. This would verify that the input is a valid identifier with a regex.For convenience this function can be variadic. The inputs will be validated and joined with
", "
.