Skip to content

Commit

Permalink
types(groq-builder): use TRootConfig instead of TSchema
Browse files Browse the repository at this point in the history
  • Loading branch information
scottrippey committed Oct 1, 2023
1 parent 5e81c85 commit 0071634
Show file tree
Hide file tree
Showing 12 changed files with 82 additions and 48 deletions.
10 changes: 5 additions & 5 deletions packages/nextjs/sanity-studio/builder/commands/deref.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import { GroqBuilder } from "../groq-builder";
import { GroqBuilder, RootConfig } from "../groq-builder";
import { ExtractRefType } from "../common-types";

declare module "../groq-builder" {
export interface GroqBuilder<TSchema, TScope> {
export interface GroqBuilder<TScope, TRootConfig extends RootConfig> {
deref(): TScope extends Array<infer TScopeItem>
? Array<ExtractRefType<TSchema, TScopeItem>>
: ExtractRefType<TSchema, TScope>;
? Array<ExtractRefType<TScopeItem, TRootConfig>>
: ExtractRefType<TScope, TRootConfig>;
}
}

GroqBuilder.implement({
deref(this: GroqBuilder<any, any>): any {
deref(this: GroqBuilder<any, RootConfig>): any {
return this.extend("->", null);
},
});
10 changes: 6 additions & 4 deletions packages/nextjs/sanity-studio/builder/commands/field.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { GroqBuilder } from "../groq-builder";
import { GroqBuilder, RootConfig } from "../groq-builder";
import { StringKeys } from "../common-types";

declare module "../groq-builder" {
export interface GroqBuilder<TSchema, TScope> {
field(all: "*"): GroqBuilder<TSchema, TScope>;
field<TFieldName extends StringKeys<keyof TScope>>(fieldName: TFieldName): GroqBuilder<TSchema, TScope[TFieldName]>;
export interface GroqBuilder<TScope, TRootConfig extends RootConfig> {
field(all: "*"): GroqBuilder<TScope, TRootConfig>;
field<TFieldName extends StringKeys<keyof TScope>>(
fieldName: TFieldName
): GroqBuilder<TScope[TFieldName], TRootConfig>;
}
}

Expand Down
30 changes: 24 additions & 6 deletions packages/nextjs/sanity-studio/builder/commands/filter.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,33 @@
import { createGroqBuilder } from "../groq-builder";
import { SanitySchemaTypes } from "../../sanity-types";
import { SanitySchemaTypes, SchemaConfig } from "../../sanity-types";
import { expectType } from "../test-utils/expectType";
import { ExtractScope } from "../common-types";
import { SanitySchemaRaw } from "../../sanity.config";
import { Simplify, SimplifyDeep } from "../type-utils";

const q = createGroqBuilder<SanitySchemaTypes>();
const q = createGroqBuilder<SchemaConfig>();

describe("filter", () => {
it("", () => {
const res = q.filterByType("flavour");
expectType<ExtractScope<typeof res>>().toStrictEqual<SanitySchemaRaw["flavour"]>();
const res = q.filter();
expectType<typeof res>().toStrictEqual<typeof q>();
expect(q).toMatchObject({
query: `[]`,
});
});
it("", () => {
const res = q.filter(`_type == 'flavour'`);
expectType<typeof res>().toStrictEqual<typeof q>();
expect(q).toMatchObject({
query: `[_type == 'flavour']`,
});
});
});

describe("filterByType", () => {
it("", () => {
const res = q.field("*").filterByType("flavour");
expectType<ExtractScope<typeof res>>().toStrictEqual<Array<SanitySchemaTypes["flavour"]>>();
expect(q).toMatchObject({
query: `*[_type == 'flavour']`,
});
});
});
11 changes: 5 additions & 6 deletions packages/nextjs/sanity-studio/builder/commands/filter.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
import { GroqBuilder } from "../groq-builder";
import { ArrayItem } from "../type-utils";

declare module "../groq-builder" {
export interface GroqBuilder<TSchema, TScope> {
filter<TScopeNew extends TScope = TScope>(filterString?: string): GroqBuilder<TSchema, TScopeNew>;
filterByType<TType extends Extract<MaybeArrayItem<TScope>, { _type: any }>["_type"]>(
export interface GroqBuilder<TScope, TRootConfig> {
filter<TScopeNew extends TScope = TScope>(filterString?: string): GroqBuilder<TScopeNew, TRootConfig>;
filterByType<TType extends Extract<ArrayItem<TScope>, { _type: any }>["_type"]>(
type: TType
): GroqBuilder<TSchema, Extract<TScope, { type: TType }>>;
): GroqBuilder<Array<Extract<ArrayItem<TScope>, { _type: TType }>>, TRootConfig>;
}
}

type MaybeArrayItem<T> = T extends Array<infer TItem> ? TItem : T;

GroqBuilder.implement({
filter(this: GroqBuilder<any, any>, filterString = "") {
return this.extend(`[${filterString}]`, null);
Expand Down
4 changes: 2 additions & 2 deletions packages/nextjs/sanity-studio/builder/commands/grab.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { createGroqBuilder } from "../groq-builder";
import { SanitySchemaTypes } from "../../sanity-types";
import { SchemaConfig } from "../../sanity-types";
import { expectType } from "../test-utils/expectType";
import { ExtractScope } from "../common-types";

const q = createGroqBuilder<SanitySchemaTypes>();
const q = createGroqBuilder<SchemaConfig>();

describe("grab", () => {
it("grab one property", () => {
Expand Down
6 changes: 3 additions & 3 deletions packages/nextjs/sanity-studio/builder/commands/grab.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,22 @@ import { GroqBuilder } from "../groq-builder";
import { ExpectedTypeError, Parser } from "../common-types";

declare module "../groq-builder" {
export interface GroqBuilder<TSchema, TScope> {
export interface GroqBuilder<TScope, TRootConfig extends RootConfig> {
grab<
TGrab extends {
[P in keyof TScope & string]?: GrabFieldConfig;
}
>(
grab: TGrab
): GroqBuilder<TSchema, SimplifyDeep<ExtractGrabResult<TScope, TGrab>>>;
): GroqBuilder<SimplifyDeep<ExtractGrabResult<TScope, TGrab>>, TRootConfig>;
}

export type GrabFieldConfig = true | Parser<any, any> | GroqBuilder<any, any>;

export type ExtractGrabResult<TScope, TGrab> = {
[P in keyof TGrab /*
Extract type from GroqBuilder:
*/]: TGrab[P] extends GroqBuilder<infer TSchema, infer TValue>
*/]: TGrab[P] extends GroqBuilder<infer TValue, infer TRootConfig>
? TValue
: /* Extract type from 'true': */
TGrab[P] extends boolean
Expand Down
28 changes: 14 additions & 14 deletions packages/nextjs/sanity-studio/builder/common-types.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,24 @@
import { GroqBuilder } from "./groq-builder";

export declare const _referenced: unique symbol;
import { GroqBuilder, RootConfig } from "./groq-builder";

/**
* A generic "parser" which can take any input and output a parsed type.
* This signature is compatible with Zod.
*/
export type Parser<TInput, TOutput> = { parse(input: TInput): TOutput };

export type RefType<TType> = {
[_referenced]: TType;
export type RefType<referencedSymbol extends symbol, TType> = {
[P in referencedSymbol]: TType;
};

export type ExtractRefType<TSchema, TScope> = TScope extends RefType<infer TType>
? Get<TSchema, TType>
: ExpectError<{
Error: "Expected the object to be a reference type";
Expected: RefType<keyof TSchema>;
Actual: TScope;
}>;
export type ExtractRefType<TScope, TRootConfig extends RootConfig> =
//
TScope extends RefType<TRootConfig["referenced"], infer TType>
? Get<TRootConfig["TSchema"], TType>
: ExpectError<{
Error: "Expected the object to be a reference type";
Expected: RefType<TRootConfig["referenced"], keyof TRootConfig["TSchema"]>;
Actual: TScope;
}>;

export type Get<TObj, TKey> = TKey extends keyof TObj
? TObj[TKey]
Expand All @@ -33,8 +33,8 @@ export type ExpectError<TError extends { Error: string; Expected: any; Actual: a
export type StringKeys<T> = Exclude<T, symbol | number>;

export type ExtractScope<TGroqBuilder extends GroqBuilder<any, any>> = TGroqBuilder extends GroqBuilder<
any,
infer TScope
infer TScope,
infer TRootConfig
>
? TScope
: never;
Expand Down
4 changes: 2 additions & 2 deletions packages/nextjs/sanity-studio/builder/groq-builder.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { createGroqBuilder } from "./groq-builder";
import { SanitySchemaTypes } from "../sanity-types";
const q = createGroqBuilder<SanitySchemaTypes>();
import { SchemaConfig } from "../sanity-types";
const q = createGroqBuilder<SchemaConfig>();

describe("filterByType", () => {
it("", () => {
Expand Down
15 changes: 9 additions & 6 deletions packages/nextjs/sanity-studio/builder/groq-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ import { Parser } from "./common-types";
import "./commands";
import { SimplifyDeep } from "./type-utils";

export function createGroqBuilder<TSchema>() {
type DocumentTypes = SimplifyDeep<TSchema[keyof TSchema]>;
return new GroqBuilder<TSchema, Array<DocumentTypes>>("", null, null);
export type RootConfig = { TSchema: any; referenced: symbol };

export function createGroqBuilder<TRootConfig extends RootConfig>() {
type TSchema = TRootConfig["TSchema"];
type RootDocumentTypes = Array<SimplifyDeep<TSchema[keyof TSchema]>>;
return new GroqBuilder<RootDocumentTypes, TRootConfig>("", null, null);
}

export class GroqBuilder<TSchema, TScope> {
export class GroqBuilder<TScope, TRootConfig extends RootConfig> {
get TScope(): TScope {
return null as any;
}
Expand All @@ -22,7 +25,7 @@ export class GroqBuilder<TSchema, TScope> {
Object.assign(GroqBuilder.prototype, methods);
}

protected readonly root: GroqBuilder<TSchema, TScope>;
protected readonly root: GroqBuilder<TScope, TRootConfig>;
constructor(
/**
*
Expand All @@ -42,7 +45,7 @@ export class GroqBuilder<TSchema, TScope> {
* This method is for chaining:
*/
protected extend<TScopeNew>(query: string, parser: Parser<any, any> | null) {
return new GroqBuilder<TSchema, TScopeNew>(this.query + query, parser, this.root);
return new GroqBuilder<TScopeNew, TRootConfig>(this.query + query, parser, this.root);
}

public async execute(fetchData: (query: string) => Promise<unknown>): Promise<TScope> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type IsEqual<A, B> = (<G>() => G extends A ? 1 : 2) extends <G>() => G ex

//
declare const RECEIVED: unique symbol;
declare const EXPECTED: unique symbol;

type Negate<Value extends boolean> = Value extends true ? false : true;

Expand Down Expand Up @@ -76,6 +77,7 @@ type TypeMatchers<Received, Inverted extends boolean = false> = {
Expected extends IsEqual<Received, Expected> extends Negate<Inverted>
? any
: {
[EXPECTED]: Expected;
[RECEIVED]: Received;
}
>(
Expand Down
7 changes: 7 additions & 0 deletions packages/nextjs/sanity-studio/builder/type-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { ExpectedTypeError } from "./common-types";

export type Simplify<T> = {
[KeyType in keyof T]: T[KeyType];
} & {};
Expand All @@ -11,3 +13,8 @@ export type SimplifyDeep<T> = T extends object
export type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void ? I : never;

export type Override<T, TOverrides> = Omit<T, keyof TOverrides> & TOverrides;

export type MaybeArrayItem<T> = T extends Array<infer TItem> ? TItem : T;
export type ArrayItem<T> = T extends Array<infer TItem>
? TItem
: ExpectedTypeError<{ error: "Expected an array"; expected: Array<any>; actual: T }>;
3 changes: 3 additions & 0 deletions packages/nextjs/sanity-studio/sanity-types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import { SanitySchemaRaw } from "./sanity.config";
// import { SimplifyDeep } from "./builder/type-utils";
import { _referenced } from "@sanity-typed/types";

/** Typescript type of all types! */
// export type SanitySchemaTypes = SimplifyDeep<SanitySchemaRaw>;
export type SanitySchemaTypes = SanitySchemaRaw;

export type SchemaConfig = { TSchema: SanitySchemaRaw; referenced: typeof _referenced };

0 comments on commit 0071634

Please sign in to comment.