1
1
import { DeepOptional , DeepReadonly , Merge } from './util.js' ;
2
2
3
- export type FieldResolverOptions < Type > = {
3
+ export type FieldResolverOptions < TypeWithTransientFields > = {
4
4
seq : number ;
5
- get : < FieldName extends keyof Type > ( fieldName : FieldName ) => Promise < Type [ FieldName ] > ;
5
+ get : < FieldName extends keyof TypeWithTransientFields > (
6
+ fieldName : FieldName ,
7
+ ) => Promise < TypeWithTransientFields [ FieldName ] > ; // FIXME: return type is wrong
6
8
} ;
7
9
8
- export class Lazy < Type , Field > {
9
- constructor ( private readonly factory : ( options : FieldResolverOptions < Type > ) => Field | Promise < Field > ) { }
10
- async get ( options : FieldResolverOptions < Type > ) : Promise < Field > {
10
+ export class Lazy < TypeWithTransientFields , Field > {
11
+ constructor (
12
+ private readonly factory : ( options : FieldResolverOptions < TypeWithTransientFields > ) => Field | Promise < Field > ,
13
+ ) { }
14
+ async get ( options : FieldResolverOptions < TypeWithTransientFields > ) : Promise < Field > {
11
15
return this . factory ( options ) ;
12
16
}
13
17
}
14
18
/** Wrapper to delay field generation until needed. */
15
- export function lazy < Type , Field > (
16
- factory : ( options : FieldResolverOptions < Type > ) => Field | Promise < Field > ,
17
- ) : Lazy < Type , Field > {
19
+ export function lazy < TypeWithTransientFields , Field > (
20
+ factory : ( options : FieldResolverOptions < TypeWithTransientFields > ) => Field | Promise < Field > ,
21
+ ) : Lazy < TypeWithTransientFields , Field > {
18
22
return new Lazy ( factory ) ;
19
23
}
20
24
21
- export type FieldResolver < Type , Field > = Field | Lazy < Type , Field > ;
25
+ export type FieldResolver < TypeWithTransientFields , Field > = Field | Lazy < TypeWithTransientFields , Field > ;
22
26
/** The type of `defaultFields` option of `defineFactory` function. */
23
- export type DefaultFieldsResolver < Type > = {
24
- [ FieldName in keyof Type ] : FieldResolver < Type , DeepReadonly < DeepOptional < Type > [ FieldName ] > > ;
27
+ export type DefaultFieldsResolver < Type , TransientFields > = {
28
+ [ FieldName in keyof Type ] : FieldResolver < Type & TransientFields , DeepReadonly < DeepOptional < Type > [ FieldName ] > > ;
29
+ } ;
30
+ /** The type of `transientFields` option of `defineFactory` function. */
31
+ export type TransientFieldsResolver < Type , TransientFields > = {
32
+ [ FieldName in keyof TransientFields ] : FieldResolver <
33
+ Type & TransientFields ,
34
+ DeepReadonly < DeepOptional < TransientFields > [ FieldName ] >
35
+ > ;
25
36
} ;
26
37
/** The type of `inputFields` option of `build` method. */
27
- export type InputFieldsResolver < Type > = {
28
- [ FieldName in keyof Type ] ?: FieldResolver < Type , DeepReadonly < DeepOptional < Type > [ FieldName ] > > ;
38
+ export type InputFieldsResolver < Type , TransientFields > = {
39
+ [ FieldName in keyof ( Type & TransientFields ) ] ?: FieldResolver <
40
+ Type & TransientFields ,
41
+ DeepReadonly < DeepOptional < Type & TransientFields > [ FieldName ] >
42
+ > ;
29
43
} ;
30
44
31
45
// eslint-disable-next-line @typescript-eslint/no-unused-vars
@@ -39,44 +53,52 @@ export type ResolvedFields<FieldsResolver extends Record<string, FieldResolver<u
39
53
40
54
export async function resolveFields <
41
55
Type extends Record < string , unknown > ,
42
- _DefaultFieldsResolver extends DefaultFieldsResolver < Type > = DefaultFieldsResolver < Type > ,
43
- _InputFieldsResolver extends InputFieldsResolver < Type > = InputFieldsResolver < Type > ,
56
+ TransientFields extends Record < string , unknown > ,
57
+ _TransientFieldsResolver extends TransientFieldsResolver < Type , TransientFields > ,
58
+ _DefaultFieldsResolver extends DefaultFieldsResolver < Type , TransientFields > ,
59
+ _InputFieldsResolver extends InputFieldsResolver < Type , TransientFields > ,
44
60
> (
45
61
seq : number ,
46
62
defaultFieldsResolver : _DefaultFieldsResolver ,
63
+ transientFieldsResolver : _TransientFieldsResolver ,
47
64
inputFieldsResolver : _InputFieldsResolver ,
48
- ) : Promise < Merge < ResolvedFields < _DefaultFieldsResolver > , ResolvedFields < _InputFieldsResolver > > > {
65
+ ) : Promise < Merge < ResolvedFields < _DefaultFieldsResolver > , Pick < ResolvedFields < _InputFieldsResolver > , keyof Type > > > {
66
+ type TypeWithTransientFields = Type & TransientFields ;
67
+
49
68
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Use any type as it is impossible to match types.
50
- const fields : any = { } ;
69
+ const fields = { } as any ;
51
70
52
- async function resolveField < Field > (
53
- options : FieldResolverOptions < Type > ,
54
- fieldResolver : FieldResolver < Type , Field > ,
55
- ) : Promise < Field > {
71
+ async function resolveField <
72
+ _FieldResolverOptions extends FieldResolverOptions < TypeWithTransientFields > ,
73
+ _FieldResolver extends FieldResolver < TypeWithTransientFields , unknown > ,
74
+ > ( options : _FieldResolverOptions , fieldResolver : _FieldResolver ) : Promise < ResolvedField < _FieldResolver > > {
56
75
if ( fieldResolver instanceof Lazy ) {
57
76
return fieldResolver . get ( options ) ;
58
77
} else {
59
- return fieldResolver ;
78
+ return fieldResolver as ResolvedField < _FieldResolver > ;
60
79
}
61
80
}
62
81
63
- async function resolveFieldAndUpdateCache < FieldName extends keyof Type > (
82
+ async function resolveFieldAndUpdateCache < FieldName extends keyof TypeWithTransientFields > (
64
83
fieldName : FieldName ,
65
- ) : Promise < Type [ FieldName ] > {
84
+ ) : Promise < ( ResolvedFields < _DefaultFieldsResolver > & ResolvedFields < _InputFieldsResolver > ) [ FieldName ] > {
66
85
if ( fieldName in fields ) return fields [ fieldName ] ;
67
86
68
- if ( fieldName in inputFieldsResolver ) {
69
- // eslint-disable-next-line require-atomic-updates, no-await-in-loop -- The fields are resolved sequentially, so there is no possibility of a race condition.
70
- fields [ fieldName ] = await resolveField ( options , inputFieldsResolver [ fieldName ] ) ;
71
- } else {
72
- // eslint-disable-next-line require-atomic-updates, no-await-in-loop -- The fields are resolved sequentially, so there is no possibility of a race condition.
73
- fields [ fieldName ] = await resolveField ( options , defaultFieldsResolver [ fieldName ] ) ;
74
- }
87
+ const fieldResolver =
88
+ fieldName in inputFieldsResolver
89
+ ? inputFieldsResolver [ fieldName as keyof _InputFieldsResolver ]
90
+ : fieldName in transientFieldsResolver
91
+ ? transientFieldsResolver [ fieldName as keyof _TransientFieldsResolver ]
92
+ : defaultFieldsResolver [ fieldName as keyof _DefaultFieldsResolver ] ;
93
+
94
+ // eslint-disable-next-line require-atomic-updates
95
+ fields [ fieldName ] = await resolveField ( options , fieldResolver ) ;
75
96
return fields [ fieldName ] ;
76
97
}
77
98
78
- const options : FieldResolverOptions < Type > = {
99
+ const options : FieldResolverOptions < TypeWithTransientFields > = {
79
100
seq,
101
+ // @ts -expect-error -- FIXME: return type is wrong
80
102
get : resolveFieldAndUpdateCache ,
81
103
} ;
82
104
@@ -85,5 +107,6 @@ export async function resolveFields<
85
107
await resolveFieldAndUpdateCache ( fieldName ) ;
86
108
}
87
109
88
- return fields ;
110
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Use any type as it is impossible to match types.
111
+ return Object . fromEntries ( Object . entries ( fields ) . filter ( ( [ key ] ) => key in defaultFieldsResolver ) ) as any ;
89
112
}
0 commit comments