1
1
/* eslint-disable @typescript-eslint/ban-types */
2
2
3
- import { ContainerKey , ContextualParamsToResolverKeys } from './util.ts' ;
3
+ export type ContainerKey = string | symbol
4
+ type ConstrainedKey = Exclude < ContainerKey , '$' | `$:${string } `>
4
5
5
6
type ExtractPrefix < S extends ContainerKey > =
6
7
S extends `${infer Prefix } :${string } ` ? Prefix : never
@@ -11,7 +12,56 @@ type ExtractPrefixedValues<
11
12
BaseKeys extends keyof Struct = keyof Struct ,
12
13
> = BaseKeys extends `${Prefix } :${infer U } ` ? Struct [ `${Prefix } :${U } `] : never
13
14
14
- type ConstrainedKey = Exclude < ContainerKey , '$' | `$:${string } `>
15
+ type KeysMatching < Collection , Value > = {
16
+ [ K in keyof Collection ] -?: Collection [ K ] extends Value ? K : never
17
+ } [ keyof Collection ]
18
+
19
+ type ContextualParamsToSyncResolverKeys <
20
+ TSyncDependencies extends Record < ConstrainedKey , unknown > ,
21
+ TAsyncDependencies extends Record < ConstrainedKey , unknown > ,
22
+ TParams extends
23
+ | readonly (
24
+ | TSyncDependencies [ keyof TSyncDependencies ]
25
+ | ReadableSyncContainer < Partial < TSyncDependencies > >
26
+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
27
+ | ReadableContainer <
28
+ Partial < TSyncDependencies > ,
29
+ Partial < TAsyncDependencies >
30
+ >
31
+ ) [ ]
32
+ | [ ] ,
33
+ > = {
34
+ [ K in keyof TParams ] : TParams [ K ] extends
35
+ | ReadableSyncContainer < Partial < TSyncDependencies > >
36
+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
37
+ ? '$'
38
+ : KeysMatching < TSyncDependencies , TParams [ K ] >
39
+ }
40
+
41
+ type ContextualParamsToAsyncResolverKeys <
42
+ TSyncDependencies extends Record < ConstrainedKey , unknown > ,
43
+ TAsyncDependencies extends Record < ConstrainedKey , unknown > ,
44
+ TParams extends
45
+ | readonly (
46
+ | TSyncDependencies [ keyof TSyncDependencies ]
47
+ | TAsyncDependencies [ keyof TAsyncDependencies ]
48
+ | ReadableSyncContainer < Partial < TSyncDependencies > >
49
+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
50
+ | ReadableContainer <
51
+ Partial < TSyncDependencies > ,
52
+ Partial < TAsyncDependencies >
53
+ >
54
+ ) [ ]
55
+ | [ ] ,
56
+ > = {
57
+ [ K in keyof TParams ] : TParams [ K ] extends
58
+ | ReadableSyncContainer < Partial < TSyncDependencies > >
59
+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
60
+ ? '$'
61
+ :
62
+ | KeysMatching < TSyncDependencies , TParams [ K ] >
63
+ | KeysMatching < TAsyncDependencies , TParams [ K ] >
64
+ }
15
65
16
66
export interface SyncDependencyFactory <
17
67
T ,
@@ -116,7 +166,9 @@ export interface WritableContainer<
116
166
TAsyncDependencies extends Record < ConstrainedKey , unknown > ,
117
167
> {
118
168
/**
119
- * Register a new synchronous dependency factory.
169
+ * Registers a new synchronous dependency factory.
170
+ * It cannot be used when self-resolution is needed. Use
171
+ * `registerConstructor` instead.
120
172
*
121
173
* @param name The "name" of the dependency (can be a symbol).
122
174
* @param dependency A dependency factory.
@@ -148,7 +200,9 @@ export interface WritableContainer<
148
200
>
149
201
150
202
/**
151
- * Register a new asynchronous dependency factory.
203
+ * Registers a new asynchronous dependency factory.
204
+ * It cannot be used when self-resolution is needed. Use
205
+ * `registerAsyncConstructor` instead.
152
206
*
153
207
* @param name The "name" of the dependency (can be a symbol).
154
208
* @param dependency A dependency factory.
@@ -179,10 +233,49 @@ export interface WritableContainer<
179
233
}
180
234
>
181
235
236
+ registerConstructor <
237
+ TName extends ConstrainedKey ,
238
+ TParams extends readonly (
239
+ | TSyncDependencies [ keyof TSyncDependencies ]
240
+ | ReadableSyncContainer < Partial < TSyncDependencies > >
241
+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
242
+ | ReadableContainer <
243
+ Partial < TSyncDependencies > ,
244
+ Partial < TAsyncDependencies >
245
+ >
246
+ ) [ ] ,
247
+ TClass extends TName extends '$' | keyof TAsyncDependencies
248
+ ? never
249
+ : TName extends keyof TSyncDependencies
250
+ ? TSyncDependencies [ TName ]
251
+ : unknown ,
252
+ TDependencies extends ContextualParamsToSyncResolverKeys <
253
+ TSyncDependencies ,
254
+ TAsyncDependencies ,
255
+ TParams
256
+ > ,
257
+ > (
258
+ name : TName ,
259
+ constructor : new ( ...args : TParams ) => TClass ,
260
+ ...args : TDependencies
261
+ ) : Container <
262
+ {
263
+ [ TK in
264
+ | keyof TSyncDependencies
265
+ | TName ] : TK extends keyof TSyncDependencies
266
+ ? TName extends TK
267
+ ? TClass
268
+ : TSyncDependencies [ TK ]
269
+ : TClass
270
+ } ,
271
+ TAsyncDependencies
272
+ >
273
+
182
274
/**
183
275
* Registers a new constructor that might have asynchronous-resolvable
184
276
* dependencies. This method is helpful when the constructor combinator is
185
- * not powerful enough (as it's only able to resolve synchronously).
277
+ * not powerful enough (as it's only able to resolve synchronously, and it
278
+ * cannot take advantage of self-resolution either).
186
279
*
187
280
* @param name The "name" of the dependency (can be a symbol).
188
281
* @param constructor A class constructor, that will be use to resolve the
@@ -196,13 +289,19 @@ export interface WritableContainer<
196
289
TParams extends readonly (
197
290
| TSyncDependencies [ keyof TSyncDependencies ]
198
291
| TAsyncDependencies [ keyof TAsyncDependencies ]
292
+ | ReadableSyncContainer < Partial < TSyncDependencies > >
293
+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
294
+ | ReadableContainer <
295
+ Partial < TSyncDependencies > ,
296
+ Partial < TAsyncDependencies >
297
+ >
199
298
) [ ] ,
200
299
TClass extends TName extends '$' | keyof TSyncDependencies
201
300
? never
202
301
: TName extends keyof TAsyncDependencies
203
302
? TAsyncDependencies [ TName ]
204
303
: unknown ,
205
- TDependencies extends ContextualParamsToResolverKeys <
304
+ TDependencies extends ContextualParamsToAsyncResolverKeys <
206
305
TSyncDependencies ,
207
306
TAsyncDependencies ,
208
307
TParams
@@ -225,7 +324,7 @@ export interface WritableContainer<
225
324
>
226
325
227
326
/**
228
- * Register an already instantiated dependency.
327
+ * Registers an already instantiated dependency.
229
328
*
230
329
* @param name The "name" of the dependency (can be a symbol).
231
330
* @param dependency An already instantiated value.
@@ -430,18 +529,77 @@ function __createContainer<
430
529
}
431
530
} ,
432
531
532
+ registerConstructor <
533
+ TName extends ConstrainedKey ,
534
+ TParams extends readonly (
535
+ | TSyncDependencies [ keyof TSyncDependencies ]
536
+ | ReadableSyncContainer < Partial < TSyncDependencies > >
537
+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
538
+ | ReadableContainer <
539
+ Partial < TSyncDependencies > ,
540
+ Partial < TAsyncDependencies >
541
+ >
542
+ ) [ ] ,
543
+ TClass extends TName extends '$' | keyof TAsyncDependencies
544
+ ? never
545
+ : TName extends keyof TSyncDependencies
546
+ ? TSyncDependencies [ TName ]
547
+ : unknown ,
548
+ TDependencies extends ContextualParamsToSyncResolverKeys <
549
+ TSyncDependencies ,
550
+ TAsyncDependencies ,
551
+ TParams
552
+ > ,
553
+ > (
554
+ name : TName ,
555
+ constructor : new ( ...args : TParams ) => TClass ,
556
+ ...args : TDependencies
557
+ ) : ContainerWithNewSyncDep < TName , TClass > {
558
+ const factory = ( container : typeof this ) => {
559
+ const resolvedParams = args . map ( ( arg ) => {
560
+ return arg === '$'
561
+ ? this
562
+ : container . resolve ( arg as keyof TSyncDependencies )
563
+ } ) as unknown as TParams
564
+
565
+ return new constructor ( ...resolvedParams )
566
+ }
567
+
568
+ if ( name in syncDependencies ) {
569
+ return __createContainer (
570
+ {
571
+ ...syncDependencies ,
572
+ [ name ] : factory ,
573
+ } ,
574
+ asyncDependencies ,
575
+ ) as ContainerWithNewSyncDep < TName , TClass >
576
+ } else {
577
+ ; ( syncDependencies as Record < TName , unknown > ) [ name ] = factory
578
+ return __createContainer (
579
+ syncDependencies ,
580
+ asyncDependencies ,
581
+ ) as ContainerWithNewSyncDep < TName , TClass >
582
+ }
583
+ } ,
584
+
433
585
registerAsyncConstructor <
434
586
TName extends ConstrainedKey ,
435
587
TParams extends readonly (
436
588
| TSyncDependencies [ keyof TSyncDependencies ]
437
589
| TAsyncDependencies [ keyof TAsyncDependencies ]
590
+ | ReadableSyncContainer < Partial < TSyncDependencies > >
591
+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
592
+ | ReadableContainer <
593
+ Partial < TSyncDependencies > ,
594
+ Partial < TAsyncDependencies >
595
+ >
438
596
) [ ] ,
439
597
TClass extends TName extends '$' | keyof TSyncDependencies
440
598
? never
441
599
: TName extends keyof TAsyncDependencies
442
600
? TAsyncDependencies [ TName ]
443
601
: unknown ,
444
- TDependencies extends ContextualParamsToResolverKeys <
602
+ TDependencies extends ContextualParamsToAsyncResolverKeys <
445
603
TSyncDependencies ,
446
604
TAsyncDependencies ,
447
605
TParams
@@ -453,7 +611,9 @@ function __createContainer<
453
611
) : ContainerWithNewAsyncDep < TName , TClass > {
454
612
const factory = async ( container : typeof this ) => {
455
613
const argPromises = args . map ( ( arg ) => {
456
- return ( arg as string ) in syncDependencies
614
+ return arg === '$'
615
+ ? this
616
+ : ( arg as string ) in syncDependencies
457
617
? container . resolve ( arg as keyof TSyncDependencies )
458
618
: container . resolveAsync ( arg as keyof TAsyncDependencies )
459
619
} )
0 commit comments