1
- import type { Jsonify } from "./jsonify" ;
1
+ import type { EmptyObject , Jsonify } from "./jsonify" ;
2
2
import type { TypedDeferredData , TypedResponse } from "./responses" ;
3
+ import type {
4
+ ClientActionFunctionArgs ,
5
+ ClientLoaderFunctionArgs ,
6
+ } from "./routeModules" ;
3
7
import { expectType } from "./typecheck" ;
4
8
import { type Expect , type Equal } from "./typecheck" ;
5
9
6
10
// prettier-ignore
7
11
/**
8
- * Infer JSON serialized data type returned by a loader or action.
12
+ * Infer JSON serialized data type returned by a loader or action, while
13
+ * avoiding deserialization if the input type if it's a clientLoader or
14
+ * clientAction that returns a non-Response
9
15
*
10
16
* For example:
11
17
* `type LoaderData = SerializeFrom<typeof loader>`
12
18
*/
13
19
export type SerializeFrom < T > =
14
- T extends ( ...args : any [ ] ) => infer Output ? Serialize < Awaited < Output > > :
20
+ T extends ( ...args : any [ ] ) => infer Output ?
21
+ Parameters < T > extends [ ClientLoaderFunctionArgs | ClientActionFunctionArgs ] ?
22
+ // Client data functions may not serialize
23
+ SerializeClient < Awaited < Output > >
24
+ :
25
+ // Serialize responses
26
+ Serialize < Awaited < Output > >
27
+ :
15
28
// Back compat: manually defined data type, not inferred from loader nor action
16
29
Jsonify < Awaited < T > >
17
30
;
18
31
32
+ // note: cannot be inlined as logic requires union distribution
33
+ // prettier-ignore
34
+ type SerializeClient < Output > =
35
+ Output extends TypedDeferredData < infer U > ?
36
+ // top-level promises
37
+ & {
38
+ [ K in keyof U as K extends symbol
39
+ ? never
40
+ : Promise < any > extends U [ K ]
41
+ ? K
42
+ : never ] : DeferValueClient < U [ K ] > ; // use generic to distribute over union
43
+ }
44
+ // non-promises
45
+ & {
46
+ [ K in keyof U as Promise < any > extends U [ K ] ? never : K ] : U [ K ] ;
47
+ }
48
+ :
49
+ Output extends TypedResponse < infer U > ? Jsonify < U > :
50
+ Awaited < Output >
51
+
52
+ // prettier-ignore
53
+ type DeferValueClient < T > =
54
+ T extends undefined ? undefined :
55
+ T extends Promise < unknown > ? Promise < Awaited < T > > :
56
+ T ;
57
+
19
58
// note: cannot be inlined as logic requires union distribution
20
59
// prettier-ignore
21
60
type Serialize < Output > =
@@ -49,16 +88,45 @@ type DeferValue<T> =
49
88
50
89
type Pretty < T > = { [ K in keyof T ] : T [ K ] } ;
51
90
52
- type Loader < T > = ( ) => Promise <
53
- | TypedResponse < T > // returned responses
54
- | TypedResponse < never > // thrown responses
55
- > ;
91
+ type Loader < T > = ( ) => Promise < TypedResponse < T > > ;
56
92
57
93
type LoaderDefer < T extends Record < keyof unknown , unknown > > = ( ) => Promise <
58
- | TypedDeferredData < T > // returned responses
59
- | TypedResponse < never > // thrown responses
94
+ TypedDeferredData < T >
95
+ > ;
96
+
97
+ type LoaderBoth <
98
+ T1 extends Record < keyof unknown , unknown > ,
99
+ T2 extends Record < keyof unknown , unknown >
100
+ > = ( ) => Promise < TypedResponse < T1 > | TypedDeferredData < T2 > > ;
101
+
102
+ type ClientLoaderRaw < T extends Record < keyof unknown , unknown > > = ( {
103
+ request,
104
+ } : ClientLoaderFunctionArgs ) => Promise < T > ; // returned non-Response
105
+
106
+ type ClientLoaderResponse < T extends Record < keyof unknown , unknown > > = ( {
107
+ request,
108
+ } : ClientLoaderFunctionArgs ) => Promise < TypedResponse < T > > ; // returned responses
109
+
110
+ type ClientLoaderDefer < T extends Record < keyof unknown , unknown > > = ( {
111
+ request,
112
+ } : ClientLoaderFunctionArgs ) => Promise < TypedDeferredData < T > > ; // returned responses
113
+
114
+ type ClientLoaderResponseAndDefer <
115
+ T1 extends Record < keyof unknown , unknown > ,
116
+ T2 extends Record < keyof unknown , unknown >
117
+ > = ( {
118
+ request,
119
+ } : ClientLoaderFunctionArgs ) => Promise <
120
+ TypedResponse < T1 > | TypedDeferredData < T2 >
60
121
> ;
61
122
123
+ type ClientLoaderRawAndDefer <
124
+ T1 extends Record < keyof unknown , unknown > ,
125
+ T2 extends Record < keyof unknown , unknown >
126
+ > = ( {
127
+ request,
128
+ } : ClientLoaderFunctionArgs ) => Promise < T1 | TypedDeferredData < T2 > > ;
129
+
62
130
// prettier-ignore
63
131
// eslint-disable-next-line @typescript-eslint/no-unused-vars
64
132
type _tests = [
@@ -78,7 +146,27 @@ type _tests = [
78
146
Expect < Equal < Pretty < SerializeFrom < Loader < { a : string , name : number , data : boolean } > > > , { a : string , name : number , data : boolean } > > ,
79
147
80
148
// defer top-level promises
81
- Expect < SerializeFrom < LoaderDefer < { a : string ; lazy : Promise < { b : number } > } > > extends { a : string , lazy : Promise < { b : number } > } ? true : false >
149
+ Expect < SerializeFrom < LoaderDefer < { a : string ; lazy : Promise < { b : number } > } > > extends { a : string , lazy : Promise < { b : number } > } ? true : false > ,
150
+
151
+ // conditional defer or json
152
+ Expect < SerializeFrom < LoaderBoth < { a :string , b : Promise < string > } , { c : string ; lazy : Promise < { d : number } > } > > extends { a : string , b : EmptyObject } | { c : string ; lazy : Promise < { d : number } > } ? true : false > ,
153
+
154
+ // clientLoader raw JSON
155
+ Expect < Equal < Pretty < SerializeFrom < ClientLoaderRaw < { a : string } > > > , { a : string } > > ,
156
+ Expect < Equal < Pretty < SerializeFrom < ClientLoaderRaw < { a : Date , b : Map < string , number > } > > > , { a : Date , b : Map < string , number > } > > ,
157
+
158
+ // clientLoader json() Response
159
+ Expect < Equal < Pretty < SerializeFrom < ClientLoaderResponse < { a : string } > > > , { a : string } > > ,
160
+ Expect < Equal < Pretty < SerializeFrom < ClientLoaderResponse < { a : Date } > > > , { a : string } > > ,
161
+
162
+ // clientLoader defer() data
163
+ Expect < SerializeFrom < ClientLoaderDefer < { a : string ; lazy : Promise < { b : number } > } > > extends { a : string , lazy : Promise < { b : number } > } ? true : false > ,
164
+
165
+ // clientLoader conditional defer or json
166
+ Expect < SerializeFrom < ClientLoaderResponseAndDefer < { a : string , b : Promise < string > } , { c : string ; lazy : Promise < { d : number } > } > > extends { a : string , b : EmptyObject } | { c : string ; lazy : Promise < { d : number } > } ? true : false > ,
167
+
168
+ // clientLoader conditional defer or raw
169
+ Expect < SerializeFrom < ClientLoaderRawAndDefer < { a : string , b : Promise < string > } , { c : string ; lazy : Promise < { d : number } > } > > extends { a : string , b : Promise < string > } | { c : string ; lazy : Promise < { d : number } > } ? true : false > ,
82
170
] ;
83
171
84
172
// recursive
0 commit comments