Closed johanbrook closed 2 years ago
@colinhacks @jaclarke
You are importing the library right, however the default export, in this case e
is a big object (not a typescript type!).
You can see this behaviour when you hover over your e
import, it shows up as a constant object (at least in VS Code).
If you wanted to get the typeof User in e
, you would do typeof e.User
, however this probably is not what you're looking for.
Importing import { $ContactλShape } from "./edgeql-js/modules/default"
might work out for you.
I'm not 100% sure on what the preferred way of getting the schema type is, hope this helps though.
Thanks for the reply!
Yea, I've toyed around with various kinds of import type
and typeof e.User
but to no avail 😄 I've read the source of the generated code, and $ContactλShape
seemed very ... non-public and scary to use in app code.
My angle is from GraphQL, where you'd generate TS types for two things:
Is it so that 1) and 2) are inferred by TS with thanks to edgeql-js? Meaning, no "concrete" types are generated, but everything is meant to be inferred when taking care of query results.
Example:
export const findContacts = async () ⇒
e.select(e.Contact, () ⇒ {
name: true,
// email: true,
}).run(client);
Array<{ name: string }>
.email
, it'd be: Array<{ name: string, email: string }>
.This is all dynamic as I manipulate the fields in the query. Which is pretty cool. If I'd have a "concrete" type for the function, it'd go out of sync easily with need for generating new TS types from the query:
// ! This would go out of sync
import type { ContactQuery } from './some-generated-schema.ts`;
export const findContacts = async (): Array<ContactQuery> ⇒ { ... }
I've solved my particular issue with this:
// db.ts
export type FindContacts = Awaited<ReturnType<typeof findContacts>>;
export const findContacts = async () ⇒ { … };
// someComponent.ts
import type { FindContacts } from './db';
const Contacts = (contacts: FindContacts) ⇒ {
return (
<ul>
{/* This is now type safe */}
{contacts.map(c ⇒ <li>{c.name}</lI>)}
</ul>
);
}
With that said, it'd be nice to get a hold of the concrete types from the schema somehow.
@sikarii's right, the default export "e" of the querybuilder is an object not a namespace. The reason is that type names in EdgeDB can contain pretty much any unicode character, so we can't export them as normal identifiers and instead everything has to be keys on an object.
Since queries in EdgeQL can be composed of any arbitrary expressions (where you can choose which properties of an object get returned, traverse links, define computed props, etc..), currently there are no real 'concrete' types in the querybuilder, and everything has to be inferred for each specific query. There is a helper type: $infer
, which you can use to get the return type that you would get if you called .run()
on any expression:
import e, {$infer} from './edgeql-js';
const query = e.select(e.Contact, () => ({
name: true,
email: true
}));
export type Contacts = $infer<typeof query>;
export async function findContacts() { // Return type would be Promise<Contacts>
return await query.run(client);
}
What kind of types would you find useful as concrete types? Something like this?:
interface AddressBook {
contacts?: Contact[];
}
interface Contact {
name?: string;
email?: string;
}
@jaclarke Gotcha, thanks. That confirms what I ultimately arrived at too.
$infer
looks really cool, thanks! No need for ReturnType
✨
As mentioned, I came from GraphQL where the server schema and client queries are generated into TS types. The latter might be hard to pull off with edgeql-js then, but the former would be handy.
Imagine this scenario:
// email.ts
// ! This is where I'd make use of a "concrete" type Contact !
import type { Contact } from './schema.ts'; // auto-generated
export const sendEmailTo(contacts: Array<Pick<Contact, 'email' | 'someOtherProp'>>) ⇒ {
// ...
};
server.ts
import { sendEmailTo } from './email';
import { findContacts } from './db';
const main = async () ⇒ {
const contacts = await findContacts();
sendEmailTo(contacts);
};
sendEmailTo
only cares about email
and someOtherProp
(I added the latter to highlight that we're really interested in the type Contact
– not just an array of emails).email
and someOtherProp
are included in the query.Seems like this is what the $XXXXXλShape
types are, in modules/default.ts
no?
The $XXXXXλShape
types are very different - that is a complex data structure representing that EdgeDB type. You can introspect what this type looks like by playing around with autocompletion:
e.Movie.__element__.__pointers__;
// $MovieλShape
We don't currently generate "plain" types like what you are describing, though I think we should. It'll probably look something like this:
import e, {types} from "./dbschema/edgeql-js";
type Movie = types["default"]["Movie"];
// {title: string; actors: Person, ...}
Should be possible to do @jaclarke thoughts? Internally it'll look something like this:
interface default$$User {
name: string;
filmography: default$$Movie[];
}
interface default$$Movie {
name: string;
actors: default$$User[];
}
interface default$$ {
User: default$$User;
Movie: default$$Movie;
}
export interface types {
default: default$$;
}
type asdf = types['default']['User']['filmography']
PR for this here: https://github.com/edgedb/edgedb-js/pull/281
Hi! Sorry for a "question issue", but I just can't get past this myself.
npx edgeql-js
to generate all code.e.select().run(client)
and so on.But when I try to do use a generated TS type from the package, I run into this TS error:
Code:
I'm following the docs in "Objects and paths", which says:
Are the docs talking about the Typescript type here? Or is it a reference to the EdgeQL schema type?
Thoughts
e
incorrectly?Versions
Many thanks!