11/* eslint-disable @typescript-eslint/ban-types */
22
3- import { ContainerKey , ContextualParamsToResolverKeys } from './util.ts' ;
3+ export type ContainerKey = string | symbol
4+ type ConstrainedKey = Exclude < ContainerKey , '$' | `$:${string } `>
45
56type ExtractPrefix < S extends ContainerKey > =
67 S extends `${infer Prefix } :${string } ` ? Prefix : never
@@ -11,7 +12,56 @@ type ExtractPrefixedValues<
1112 BaseKeys extends keyof Struct = keyof Struct ,
1213> = BaseKeys extends `${Prefix } :${infer U } ` ? Struct [ `${Prefix } :${U } `] : never
1314
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+ }
1565
1666export interface SyncDependencyFactory <
1767 T ,
@@ -116,7 +166,9 @@ export interface WritableContainer<
116166 TAsyncDependencies extends Record < ConstrainedKey , unknown > ,
117167> {
118168 /**
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.
120172 *
121173 * @param name The "name" of the dependency (can be a symbol).
122174 * @param dependency A dependency factory.
@@ -148,7 +200,9 @@ export interface WritableContainer<
148200 >
149201
150202 /**
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.
152206 *
153207 * @param name The "name" of the dependency (can be a symbol).
154208 * @param dependency A dependency factory.
@@ -179,10 +233,49 @@ export interface WritableContainer<
179233 }
180234 >
181235
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+
182274 /**
183275 * Registers a new constructor that might have asynchronous-resolvable
184276 * 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).
186279 *
187280 * @param name The "name" of the dependency (can be a symbol).
188281 * @param constructor A class constructor, that will be use to resolve the
@@ -196,13 +289,19 @@ export interface WritableContainer<
196289 TParams extends readonly (
197290 | TSyncDependencies [ keyof TSyncDependencies ]
198291 | TAsyncDependencies [ keyof TAsyncDependencies ]
292+ | ReadableSyncContainer < Partial < TSyncDependencies > >
293+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
294+ | ReadableContainer <
295+ Partial < TSyncDependencies > ,
296+ Partial < TAsyncDependencies >
297+ >
199298 ) [ ] ,
200299 TClass extends TName extends '$' | keyof TSyncDependencies
201300 ? never
202301 : TName extends keyof TAsyncDependencies
203302 ? TAsyncDependencies [ TName ]
204303 : unknown ,
205- TDependencies extends ContextualParamsToResolverKeys <
304+ TDependencies extends ContextualParamsToAsyncResolverKeys <
206305 TSyncDependencies ,
207306 TAsyncDependencies ,
208307 TParams
@@ -225,7 +324,7 @@ export interface WritableContainer<
225324 >
226325
227326 /**
228- * Register an already instantiated dependency.
327+ * Registers an already instantiated dependency.
229328 *
230329 * @param name The "name" of the dependency (can be a symbol).
231330 * @param dependency An already instantiated value.
@@ -430,18 +529,77 @@ function __createContainer<
430529 }
431530 } ,
432531
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+
433585 registerAsyncConstructor <
434586 TName extends ConstrainedKey ,
435587 TParams extends readonly (
436588 | TSyncDependencies [ keyof TSyncDependencies ]
437589 | TAsyncDependencies [ keyof TAsyncDependencies ]
590+ | ReadableSyncContainer < Partial < TSyncDependencies > >
591+ | ReadableAsyncContainer < Partial < TAsyncDependencies > >
592+ | ReadableContainer <
593+ Partial < TSyncDependencies > ,
594+ Partial < TAsyncDependencies >
595+ >
438596 ) [ ] ,
439597 TClass extends TName extends '$' | keyof TSyncDependencies
440598 ? never
441599 : TName extends keyof TAsyncDependencies
442600 ? TAsyncDependencies [ TName ]
443601 : unknown ,
444- TDependencies extends ContextualParamsToResolverKeys <
602+ TDependencies extends ContextualParamsToAsyncResolverKeys <
445603 TSyncDependencies ,
446604 TAsyncDependencies ,
447605 TParams
@@ -453,7 +611,9 @@ function __createContainer<
453611 ) : ContainerWithNewAsyncDep < TName , TClass > {
454612 const factory = async ( container : typeof this ) => {
455613 const argPromises = args . map ( ( arg ) => {
456- return ( arg as string ) in syncDependencies
614+ return arg === '$'
615+ ? this
616+ : ( arg as string ) in syncDependencies
457617 ? container . resolve ( arg as keyof TSyncDependencies )
458618 : container . resolveAsync ( arg as keyof TAsyncDependencies )
459619 } )
0 commit comments