Created
November 16, 2020 14:33
-
-
Save mcky/4321f9d2c99667c715c7fd8b0d458e6f to your computer and use it in GitHub Desktop.
typed groq queries
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copied from https://twitter.com/MikeRyanDev/status/1308472279010025477 | |
// Don't need most of the token parsing, but leaving it in until the POC is complete because I'll probably | |
// break it all trying to remove it | |
type Split<S extends string, D extends string> = S extends `${infer T}${D}${infer U}` ? [T, ...Split<U, D>] : [S]; | |
type TakeLast<V> = V extends [] ? never : V extends [string] ? V[0] : V extends [string, ...infer R] ? TakeLast<R> : never; | |
type TrimLeft<V extends string> = V extends ` ${infer R}` ? TrimLeft<R> : V; | |
type TrimRight<V extends string> = V extends `${infer R} ` ? TrimRight<R> : V; | |
type Trim<V extends string> = TrimLeft<TrimRight<V>>; | |
type StripModifier<V extends string, M extends string> = V extends `${infer L}${M}${infer A}` ? L : V; | |
type StripModifiers<V extends string> = StripModifier<StripModifier<StripModifier<StripModifier<V, '.'>, '#'>, '['>, ':'>; | |
type TakeLastAfterToken<V extends string, T extends string> = StripModifiers<TakeLast<Split<Trim<V>, T>>>; | |
type GetLastElementName<V extends string> = TakeLastAfterToken<TakeLastAfterToken<V, ' '>, '>'>; | |
type GetEachElementName<V, L extends string[] = []> = | |
V extends [] | |
? L | |
: V extends [string] | |
? [...L, GetLastElementName<V[0]>] | |
: V extends [string, ...infer R] | |
? GetEachElementName<R, [...L, GetLastElementName<V[0]>]> | |
: []; | |
// Split and trim | |
type GetElementNames<V extends string> = GetEachElementName<Split<V, ','>>; | |
type _debug_keyList = GetElementNames<"_id, title"> | |
type ParseQueryBody<T extends string, S> = GetElementNames<T>[number] extends keyof S | |
? Pick<S, GetElementNames<T>[number]> | |
: never | |
type _debug_matchesSingular = ParseQueryBody<'_id', CommonIssue> | |
type _debug_matchesMultiple = ParseQueryBody<'_id, title', CommonIssue> | |
type _debug_matchesMultiple2 = ParseQueryBody<'_id, name', CommonIssue> | |
type ExtractByType<T, S extends string> = Extract<T, { _type: S}> | |
type Parse<T extends string, S> = | |
T extends `[_type == "${infer Type}"${infer RestQ}] {${infer Rest}}` | |
? ParseQueryBody<Rest, ExtractByType<S, Type>> | |
: never | |
// Test types | |
interface SanityDocument { | |
_id: string; | |
_createdAt: string; | |
_rev: string; | |
_updatedAt: string; | |
} | |
export interface PlantSpecies extends SanityDocument { | |
_type: "plantSpecies", | |
name: string; | |
} | |
export interface CommonIssue extends SanityDocument { | |
_type: "commonIssue" | |
title: string; | |
description: number; | |
} | |
type Documents = PlantSpecies | CommonIssue | |
type _debug_plants = Parse<'[_type == "plantSpecies"] { _id, name }', Documents> | |
type _debug_issues = Parse<'[_type == "commonIssue" && _id == 5] { _id, title }', Documents> | |
type _debug_issues_invalid_query = Parse<'[_type == "commonIssue" && _id == 5] { _id, title, nonexistant }', Documents> | |
declare function query<S extends string>(q: S): Parse<S, Documents>; | |
const invalidPlants = query('[_type == "plantSpecies"] { _id, name, foo }') | |
invalidPlants.name | |
const validPlants = query('[_type == "plantSpecies"] { _id, name }') | |
validPlants.name | |
const issuesWithoutDescriptions = query('[_type == "commonIssue" && _id == 5] { _id, title }') | |
issuesWithoutDescriptions.title | |
issuesWithoutDescriptions.description |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment