Skip to content

Instantly share code, notes, and snippets.

@latant
Last active February 25, 2022 15:14
Show Gist options
  • Save latant/8ae40787ba79f0a632d10f3e7a0345cb to your computer and use it in GitHub Desktop.
Save latant/8ae40787ba79f0a632d10f3e7a0345cb to your computer and use it in GitHub Desktop.
Creating graphql-like type-safe construct using the advanced types of TypeScript
type PropertyType = "string" | "number" | "date";
type LinkSchema = { incoming: string } | { outgoing: string };
type PropertySchema =
| { nullable: PropertyType | [PropertyType] }
| { required: PropertyType | [PropertyType] };
type NodeRefSchema<L> = ({ nullable: L } | { required: L | [L] }) & LinkSchema;
type FieldSchema<L> = PropertySchema | NodeRefSchema<L>;
type NodeSchema<L> = { [key: string]: FieldSchema<L> };
type GraphSchema<L> = { [key: string]: NodeSchema<L> };
type ElementOf<A> = A extends (infer E)[] ? E : A;
type Query<S extends GraphSchema<keyof S>, K extends keyof S> = {
[F in keyof S[K]]?: QueryField<S, S[K][F]>;
};
type QueryField<
S extends GraphSchema<keyof S>,
F extends FieldSchema<keyof S>
> = F extends PropertySchema
? boolean
: F extends { required: [keyof S] }
? Query<S, ElementOf<F["required"]>>
: F extends { required: keyof S }
? Query<S, F["required"]>
: null;
class Graph<S extends GraphSchema<keyof S>> {
private schema: S;
constructor(schema: S) {
this.schema = schema;
}
query<L extends keyof S>(label: L, query: Query<S, L>) {}
}
const graph = new Graph({
User: {
nicknames: { nullable: ["string"] },
name: { required: "string" },
articles: { required: ["Article"], outgoing: "WROTE_ARTICLE" },
},
Article: {
title: { required: "string" },
writer: { required: "User", incoming: "WROTE_ARTICLE" },
},
});
graph.query("User", {
name: true,
articles: {
title: true,
writer: {
name: true,
},
},
nicknames: true,
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment