Skip to content

Commit 91bd73c

Browse files
Copilotxperiandri
andauthored
Document SRTP wrapper dispatch and finalize type safety changes
Agent-Logs-Url: https://github.com/fsprojects/FSharp.Data.GraphQL/sessions/7e806543-4139-4145-940a-64322cdd35cd Co-authored-by: xperiandri <2365592+xperiandri@users.noreply.github.com>
1 parent 98cbded commit 91bd73c

4 files changed

Lines changed: 35 additions & 18 deletions

File tree

src/FSharp.Data.GraphQL.Shared/SchemaDefinitions.fs

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,7 @@ module SchemaDefinitions =
392392
| false, _ -> getParseError destinationType s
393393
| InlineConstant value -> value.GetCoerceError destinationType
394394

395-
type TypeWrapperDispatch =
395+
type TypeWrapperStaticDispatch =
396396
static member Nullable<'Val>(innerDef : InputOutputDef<'Val>) : NullableDef<'Val> =
397397
let ofType : TypeDef<'Val> = upcast innerDef
398398
upcast { NullableDefinition.OfType = ofType }
@@ -431,24 +431,33 @@ module SchemaDefinitions =
431431

432432
/// Wraps a GraphQL input or output type definition, allowing defining field/argument
433433
/// to take option of provided value while preserving input/output kind of wrapped type.
434-
let inline Nullable< ^Def, ^Wrapped when (^Def or TypeWrapperDispatch) : (static member Nullable : ^Def -> ^Wrapped) >
434+
/// Input wrappers produce input definitions, output wrappers produce output definitions,
435+
/// and wrappers over types implementing both kinds keep both capabilities.
436+
/// Dispatch is selected at compile time via SRTP.
437+
let inline Nullable< ^Def, ^Wrapped when (^Def or TypeWrapperStaticDispatch) : (static member Nullable : ^Def -> ^Wrapped) >
435438
(innerDef : ^Def)
436439
: ^Wrapped =
437-
((^Def or TypeWrapperDispatch) : (static member Nullable : ^Def -> ^Wrapped) innerDef)
440+
((^Def or TypeWrapperStaticDispatch) : (static member Nullable : ^Def -> ^Wrapped) innerDef)
438441

439442
/// Wraps a GraphQL input or output type definition, allowing defining field/argument
440443
/// to take voption of provided value while preserving input/output kind of wrapped type.
441-
let inline StructNullable< ^Def, ^Wrapped when (^Def or TypeWrapperDispatch) : (static member StructNullable : ^Def -> ^Wrapped) >
444+
/// Input wrappers produce input definitions, output wrappers produce output definitions,
445+
/// and wrappers over types implementing both kinds keep both capabilities.
446+
/// Dispatch is selected at compile time via SRTP.
447+
let inline StructNullable< ^Def, ^Wrapped when (^Def or TypeWrapperStaticDispatch) : (static member StructNullable : ^Def -> ^Wrapped) >
442448
(innerDef : ^Def)
443449
: ^Wrapped =
444-
((^Def or TypeWrapperDispatch) : (static member StructNullable : ^Def -> ^Wrapped) innerDef)
450+
((^Def or TypeWrapperStaticDispatch) : (static member StructNullable : ^Def -> ^Wrapped) innerDef)
445451

446452
/// Wraps a GraphQL input or output type definition, allowing defining field/argument
447453
/// to take collection of provided value while preserving input/output kind of wrapped type.
448-
let inline ListOf< ^Def, ^Wrapped when (^Def or TypeWrapperDispatch) : (static member ListOf : ^Def -> ^Wrapped) >
454+
/// Input wrappers produce input definitions, output wrappers produce output definitions,
455+
/// and wrappers over types implementing both kinds keep both capabilities.
456+
/// Dispatch is selected at compile time via SRTP.
457+
let inline ListOf< ^Def, ^Wrapped when (^Def or TypeWrapperStaticDispatch) : (static member ListOf : ^Def -> ^Wrapped) >
449458
(innerDef : ^Def)
450459
: ^Wrapped =
451-
((^Def or TypeWrapperDispatch) : (static member ListOf : ^Def -> ^Wrapped) innerDef)
460+
((^Def or TypeWrapperStaticDispatch) : (static member ListOf : ^Def -> ^Wrapped) innerDef)
452461

453462
let internal variableOrElse other (_ : InputExecutionContextProvider) value (variables : IReadOnlyDictionary<string, obj>) =
454463
match value with

src/FSharp.Data.GraphQL.Shared/SchemaDefinitionsExtensions.fs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ type internal CustomFieldsObjectDefinition<'Val> (source : ObjectDef<'Val>, fiel
2727
member _.Implements = source.Implements
2828
member _.IsTypeOf = source.IsTypeOf
2929
interface TypeDef with
30+
// We construct wrappers directly here because this API works with untyped TypeDef values.
31+
// The public ListOf/Nullable helpers use SRTP dispatch and require statically known direction.
3032
member this.MakeList () = upcast { ListOfDefinition.OfType = this }
3133
member this.MakeNullable () = upcast { NullableDefinition.OfType = this }
3234
member _.Type = (source :> TypeDef).Type

src/FSharp.Data.GraphQL.Shared/TypeSystem.fs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -609,17 +609,20 @@ and OutputDef<'Val> =
609609
inherit TypeDef<'Val>
610610
end
611611

612-
/// Representation of all type definitions, that can be used as both inputs and outputs.
612+
/// Representation of type definitions that can be used as both inputs and outputs
613+
/// (for example scalars and enums). This marker is also used by SRTP wrapper dispatch.
613614
and InputOutputDef =
614615
interface
615616
inherit InputDef
616617
inherit OutputDef
617618
end
618619

619-
/// Representation of all type definitions, that can be used as both inputs and outputs.
620+
/// Representation of all type definitions, that can be used as both inputs and outputs
621+
/// and are constrained to represent the provided .NET type.
620622
and InputOutputDef<'Val> =
621623
interface
622624
inherit InputOutputDef
625+
inherit TypeDef<'Val>
623626
inherit InputDef<'Val>
624627
inherit OutputDef<'Val>
625628
end

tests/FSharp.Data.GraphQL.Tests/TypeWrappersKindSafetyTests.fs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,21 @@ let private OutputOnlyType =
2020

2121
[<Fact>]
2222
let ``ListOf keeps input-output direction`` () =
23-
let _ : InputDef<InputOnly list> = ListOf InputOnlyType
24-
let _ : OutputDef<OutputOnly list> = ListOf OutputOnlyType
25-
Assert.True true
23+
let inputList : InputDef<InputOnly list> = ListOf InputOnlyType
24+
let outputList : OutputDef<OutputOnly list> = ListOf OutputOnlyType
25+
Assert.Equal ("[InputOnlyType!]!", inputList.ToString ())
26+
Assert.Equal ("[OutputOnlyType!]!", outputList.ToString ())
2627

2728
[<Fact>]
2829
let ``Nullable keeps input-output direction`` () =
29-
let _ : InputDef<InputOnly option> = Nullable InputOnlyType
30-
let _ : OutputDef<OutputOnly option> = Nullable OutputOnlyType
31-
Assert.True true
30+
let nullableInput : InputDef<InputOnly option> = Nullable InputOnlyType
31+
let nullableOutput : OutputDef<OutputOnly option> = Nullable OutputOnlyType
32+
Assert.Equal ("InputOnlyType", nullableInput.ToString ())
33+
Assert.Equal ("OutputOnlyType", nullableOutput.ToString ())
3234

3335
[<Fact>]
3436
let ``StructNullable keeps input-output direction`` () =
35-
let _ : InputDef<InputOnly voption> = StructNullable InputOnlyType
36-
let _ : OutputDef<OutputOnly voption> = StructNullable OutputOnlyType
37-
Assert.True true
37+
let nullableInput : InputDef<InputOnly voption> = StructNullable InputOnlyType
38+
let nullableOutput : OutputDef<OutputOnly voption> = StructNullable OutputOnlyType
39+
Assert.Equal ("InputOnlyType", nullableInput.ToString ())
40+
Assert.Equal ("OutputOnlyType", nullableOutput.ToString ())

0 commit comments

Comments
 (0)