Skip to content

Instantly share code, notes, and snippets.

@icanhasjonas
Created May 2, 2019 23:21
Show Gist options
  • Save icanhasjonas/fde5a52dcc44d51f94d0b2184564b225 to your computer and use it in GitHub Desktop.
Save icanhasjonas/fde5a52dcc44d51f94d0b2184564b225 to your computer and use it in GitHub Desktop.
type ExtractArgType<T> = T extends ArgBuilder<infer U> ? U : T
type ExtractTypeBuilderType<T> = T extends TypeBuilder<infer U> ? U : T
type ExtractFieldType<T> = T extends FieldBuilder<string, {}, infer U, boolean> ? U : T
type AcceptedType = string | number | boolean | TypeBuilder<any>
class SchemaBuilder<T = {}> {
query<Builder extends TypeBuilder<{}>>(builder: (typeBuilder: TypeBuilder) => Builder) {
const result = builder(new TypeBuilder())
return (this as unknown) as SchemaBuilder<T & ExtractTypeBuilderType<Builder>>
}
}
class TypeBuilder<T = {}> {
description(text: string) {
return this
}
field<FieldName extends string, Builder extends FieldBuilder<FieldName, {}, any, true>>(name: FieldName, builder: (fieldBuilder: FieldBuilder<FieldName>) => Builder) {
const result = builder(new FieldBuilder<FieldName>())
return (this as unknown) as TypeBuilder<T & { [P in FieldName]: ExtractFieldType<Builder> }>
}
}
class FieldBuilder<FieldName extends string, ResolverArgs extends {} = {}, ResolvedType = never, IsResolved extends boolean = false> {
description(text: string) {
return this
}
type<T>() {
return (this as unknown) as FieldBuilder<FieldName, ResolverArgs, T, IsResolved>
}
arg<ArgName extends string, TArg extends ArgBuilder<any> = ArgBuilder<string>>(name: ArgName, builder?: (argBuilder: ArgBuilder) => TArg) {
return (this as unknown) as FieldBuilder<FieldName, ResolverArgs & { [P in ArgName]: ExtractArgType<TArg> }, ResolvedType, IsResolved>
}
resolve<T extends Promise<ResolvedType> | ResolvedType>(resolver: (args: ResolverArgs) => T) {
return (this as unknown) as FieldBuilder<FieldName, ResolverArgs, ResolvedType, true>
}
}
class ArgBuilder<T = string> {
description(text: string) {
return this
}
type<ArgType extends AcceptedType>(t?: ArgType) {
return (this as unknown) as ArgBuilder<ExtractTypeBuilderType<ArgType>>
}
required() {
return this
}
default(value: ExtractTypeBuilderType<T>) {
return this
}
}
function createRangeQuery<T>() {
return new TypeBuilder()
.field('eq', x => x.type<T>().description('match exactly'))
.field('neq', x => x.type<T>().description('don\'t match'))
.field('lt', x => x.type<T>().description('less than'))
.field('lte', x => x.type<T>().description('less than or equal'))
.field('gt', x => x.type<T>().description('greater than'))
.field('gte', x => x.type<T>().description('greater than or equal'))
.field('in', x => x.type<T[]>().description('in set'))
.field('nin', x => x.type<T[]>().description('not in set'))
}
const s = new SchemaBuilder().query(q =>
q
.description('This is the query')
.field('hello', f =>
f
.type<string>()
.description('greet someone with {name}')
.arg('name', x => x.required().description('the name of the greeting'))
.arg('filter', x =>
x
.required()
.description('any filter of stuff')
.type(createRangeQuery<number>()),
)
.arg('repeat', x =>
x
.description('number of times to repeat greeting')
.type<number>()
.default(1),
)
.resolve(async ({ name, repeat, filter }) => {
return `Hello, ${name}`
}),
)
.field('apa', x => x),
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment