Skip to content

Commit cd8f4bd

Browse files
committed
fix: custom sel. set return type for array custom types (#404)
1 parent 815d5e8 commit cd8f4bd

File tree

5 files changed

+1155
-635
lines changed

5 files changed

+1155
-635
lines changed

.changeset/large-squids-rescue.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@aws-amplify/data-schema': patch
3+
---
4+
5+
fix: custom sel. set return type for array custom types

packages/data-schema/src/runtime/client/index.ts

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -84,21 +84,52 @@ type ReturnValue<
8484
* This mapped type traverses the SelectionSetReturnValue result and the original FlatModel, restoring array types
8585
* that were flattened in DeepPickFromPath
8686
*
87-
* Note: custom type field arrays are already handled correctly and don't need to be "restored", hence the `Result[K] extends Array<any>` check
87+
* @typeParam Result - this is the result of applying the selection set path to FlatModel; return type of UnionToIntersection<DeepPickFromPath<FlatModel, Paths>>
88+
* @typeParam FlatModel - the reference model shape; return type of ResolvedModel<Model>
8889
*
90+
* Note: we wrap `Result` and `FlatModel` in NonNullable, because recursive invocations of this mapped type
91+
* can result in the type arguments containing `{} | null | undefined` which breaks indexed access, e.g. Result[K]
92+
*
93+
* Using NonNullable<> directly inside the mapped type is significantly more performant here than attempting to pre-compute in the type params,
94+
* e.g., `type RestoreArrays<Result, FlatModel, NonNullableResult = NonNullable<Result>, NonNullableFlatModel = NonNullable<FlatModel>> = {...}`
8995
*/
9096
type RestoreArrays<Result, FlatModel> = {
91-
[K in keyof Result]: K extends keyof FlatModel
92-
? FlatModel[K] extends Array<any>
93-
? Result[K] extends Array<any>
94-
? Result[K]
95-
: Array<RestoreArrays<Result[K], UnwrapArray<FlatModel[K]>>>
96-
: FlatModel[K] extends Record<string, any>
97-
? RestoreArrays<Result[K], FlatModel[K]>
98-
: Result[K]
97+
[K in keyof NonNullable<Result>]: K extends keyof NonNullable<FlatModel>
98+
? Array<any> extends NonNullable<FlatModel>[K]
99+
? HandleArrayNullability<
100+
NonNullable<Result>[K],
101+
NonNullable<FlatModel>[K]
102+
>
103+
: NonNullable<FlatModel>[K] extends Record<string, any>
104+
? RestoreArrays<NonNullable<Result>[K], NonNullable<FlatModel>[K]>
105+
: NonNullable<Result>[K]
99106
: never;
100107
};
101108

109+
/**
110+
* This mapped type gets called by RestoreArrays<T, K> and it restores the expected
111+
* nullability in array fields (e.g. nullable vs. required value & nullable vs. required array)
112+
*/
113+
type HandleArrayNullability<Result, FlatModel> =
114+
Array<any> extends Result
115+
? // If Result is already an array, return it as is.
116+
Result
117+
: NonNullable<FlatModel> extends Array<infer InnerValue>
118+
? // is the array nullable?
119+
null extends FlatModel
120+
? // is the value nullable?
121+
null extends InnerValue
122+
? // value and array are nullable - a.ref('SomeType').array()
123+
Array<RestoreArrays<Result, UnwrapArray<FlatModel>> | null> | null
124+
: // value required; array nullable - a.ref('SomeType').required().array()
125+
Array<RestoreArrays<Result, UnwrapArray<FlatModel>>> | null
126+
: null extends InnerValue
127+
? // value nullable; array required - a.ref('SomeType').array().required()
128+
Array<RestoreArrays<Result, UnwrapArray<FlatModel>> | null>
129+
: // value required; array required - a.ref('SomeType').required().array().required()
130+
Array<RestoreArrays<Result, UnwrapArray<FlatModel>>>
131+
: never;
132+
102133
/**
103134
* Generates flattened, readonly return type using specified custom sel. set
104135
*/

0 commit comments

Comments
 (0)