From e615ccdf10ed0037e282546bf271171846b4f8cf Mon Sep 17 00:00:00 2001 From: scottrippey Date: Tue, 6 Feb 2024 09:03:27 -0600 Subject: [PATCH] feature(variables): support deeply-nested parameters --- .../src/types/groq-expressions.test.ts | 61 ++++++++++++++++--- .../src/types/groq-expressions.ts | 36 ++++++----- 2 files changed, 75 insertions(+), 22 deletions(-) diff --git a/packages/groq-builder/src/types/groq-expressions.test.ts b/packages/groq-builder/src/types/groq-expressions.test.ts index 605fb42..12de370 100644 --- a/packages/groq-builder/src/types/groq-expressions.test.ts +++ b/packages/groq-builder/src/types/groq-expressions.test.ts @@ -1,6 +1,7 @@ import { describe, expectTypeOf, it } from "vitest"; import { Expressions } from "./groq-expressions"; import { QueryConfig, RootQueryConfig } from "./schema-types"; +import { Simplify } from "./utils"; describe("Expressions", () => { it("literal values are properly escaped", () => { @@ -117,22 +118,30 @@ describe("Expressions", () => { type ManyParameters = { str1: string; str2: string; - num: number; + num1: number; bool: boolean; }; it("we can extract parameters based on their type", () => { + type ParameterEntries = Expressions.ParameterEntries; + expectTypeOf>().toEqualTypeOf<{ + $str1: string; + $str2: string; + $num1: number; + $bool: boolean; + }>(); + expectTypeOf< - Expressions.ParametersOfType + Expressions.StringKeysWithType >().toEqualTypeOf<"$str1" | "$str2">(); expectTypeOf< - Expressions.ParametersOfType + Expressions.StringKeysWithType >().toEqualTypeOf<"$str1" | "$str2">(); expectTypeOf< - Expressions.ParametersOfType - >().toEqualTypeOf<"$num">(); + Expressions.StringKeysWithType + >().toEqualTypeOf<"$num1">(); expectTypeOf< - Expressions.ParametersOfType + Expressions.StringKeysWithType >().toEqualTypeOf<"$bool">(); }); @@ -144,7 +153,7 @@ describe("Expressions", () => { | "foo == $str2" | "foo == (string)" | `foo == "${string}"` - | "bar == $num" + | "bar == $num1" | "bar == (number)" | `bar == ${number}` | "baz == $bool" @@ -152,5 +161,43 @@ describe("Expressions", () => { | "baz == false" >(); }); + + type NestedParameters = { + nested: { + str1: string; + deep: { + str2: string; + num1: number; + }; + }; + }; + it("should work with deeply-nested parameters", () => { + type Item = { foo: string; bar: number; baz: boolean }; + type Res = Expressions.Equality>; + + type StandardSuggestions = + | `foo == (string)` + | `foo == "${string}"` + | `bar == (number)` + | `bar == ${number}` + | "baz == true" + | "baz == false"; + expectTypeOf>().toEqualTypeOf< + | "foo == $nested.str1" + | "foo == $nested.deep.str2" + | "bar == $nested.deep.num1" + >(); + }); + + it("we can extract parameters based on their type", () => { + type ParameterEntries = Expressions.ParameterEntries; + expectTypeOf>().toEqualTypeOf<{ + $nested: NestedParameters["nested"]; + "$nested.str1": string; + "$nested.deep": NestedParameters["nested"]["deep"]; + "$nested.deep.str2": string; + "$nested.deep.num1": number; + }>(); + }); }); }); diff --git a/packages/groq-builder/src/types/groq-expressions.ts b/packages/groq-builder/src/types/groq-expressions.ts index c2ef460..87c3872 100644 --- a/packages/groq-builder/src/types/groq-expressions.ts +++ b/packages/groq-builder/src/types/groq-expressions.ts @@ -1,6 +1,6 @@ import { QueryConfig } from "./schema-types"; import type { IsLiteral, LiteralUnion } from "type-fest"; -import { UndefinedToNull, ValueOf } from "./utils"; +import { StringKeys, UndefinedToNull, ValueOf } from "./utils"; import { Path, PathValue } from "./path-types"; // eslint-disable-next-line @typescript-eslint/no-namespace @@ -31,12 +31,13 @@ export namespace Expressions { export type Equality< TResultItem, - TQueryConfig extends QueryConfig + TQueryConfig extends QueryConfig, + _ParameterEntries = ParameterEntries > = ValueOf<{ [Key in SuggestedKeys]: `${Key} == ${ // First, suggest parameters: - | ParametersOfType< - TQueryConfig["parameters"], + | StringKeysWithType< + _ParameterEntries, SuggestedKeysValue > // Next, make some literal suggestions: @@ -70,15 +71,20 @@ export namespace Expressions { TKey extends SuggestedKeys > = UndefinedToNull>; - export type ParametersOfType = `$${AsString< - KeysOfType - >}`; - type KeysOfType = ValueOf<{ - [P in keyof TObject]: TObject[P] extends TType - ? P - : TType extends TObject[P] - ? P - : never; - }>; - type AsString = Extract; + export type ParameterEntries = { + [P in Path as `$${P}`]: PathValue; + }; + + /** + * Finds all (string) keys of TObject where the value matches the given TType + */ + export type StringKeysWithType = StringKeys< + ValueOf<{ + [P in keyof TObject]: TObject[P] extends TType + ? P + : TType extends TObject[P] + ? P + : never; + }> + >; }