@@ -6,28 +6,70 @@ import { defineTypedMetadata, getOwnTypedMetadata, getTypedMetadata } from '../.
66/**
77 * Property decorator to inject a dependency.
88 *
9- * @param {Class } Injection - The class of component to inject.
10- * @param expression - The expression to evaluate on the injected class.
11- * @returns {PropertyDecorator } - The property decorator function.
9+ * Supports three syntaxes:
10+ * 1. Class injection: `@Inject(UserService)`
11+ * 2. String injection with expression: `@Inject('UserService', (s) => s.getUsers())`
12+ * 3. Tuple injection (helper pattern): `@Inject(ulak('/chat'))` where ulak() returns [serviceName, expression]
13+ *
14+ * @param {Class | string | readonly [string, (injectedClass: any) => any] } Injection - The dependency to inject
15+ * @param expression - Optional expression to evaluate on the injected class (only used with Class or string)
16+ * @returns {PropertyDecorator } - The property decorator function
17+ *
18+ * @example
19+ * ```typescript
20+ * // Class injection
21+ * @Inject (UserService)
22+ * private userService: UserService;
23+ *
24+ * // String injection with expression
25+ * @Inject ('UserService', (s) => s.getAllUsers())
26+ * private users: User[];
27+ *
28+ * // Tuple injection (helper pattern)
29+ * @Inject (ulak('/chat'))
30+ * private chat: Ulak.NameSpace<'/chat'>;
31+ * ```
1232 */
13- export const Inject = ( Injection : Class | string , expression ?: ( injectedClass : any ) => any ) : PropertyDecorator => {
33+ export const Inject = (
34+ Injection : Class | string | readonly [ string | Class , ( injectedClass : any ) => any ] ,
35+ expression ?: ( injectedClass : any ) => any ,
36+ ) : PropertyDecorator => {
1437 return ( target : object , propertyKey : string ) : void => {
38+ let dependencyName : string ;
39+ let resolvedExpression : ( ( injectedClass : any ) => any ) | undefined = expression ;
40+
41+ // Check if Injection is a tuple [serviceName, expression]
42+ if ( Array . isArray ( Injection ) && Injection . length === 2 ) {
43+ const [ name , tupleExpression ] = Injection ;
44+
45+ // Check Name type, it can be a Class or String
46+ dependencyName = typeof name === 'string' ? name : getTypedMetadata < string > ( ComponentConstants . NameKey , name ) ;
47+
48+ resolvedExpression = tupleExpression ;
49+ } else if ( typeof Injection === 'string' ) {
50+ dependencyName = Injection ;
51+ } else {
52+ // Injection is a Class
53+ dependencyName = getTypedMetadata < string > ( ComponentConstants . NameKey , Injection ) ;
54+ }
55+
56+ // Store dependency
1557 const dependencies : Dependencies =
1658 getOwnTypedMetadata < Dependencies > ( ComponentConstants . DependencyKey , target . constructor ) || { } ;
1759
1860 if ( ! dependencies [ propertyKey ] ) {
19- dependencies [ propertyKey ] =
20- typeof Injection === 'string' ? Injection : getTypedMetadata < string > ( ComponentConstants . NameKey , Injection ) ;
61+ dependencies [ propertyKey ] = dependencyName ;
2162 }
2263
2364 defineTypedMetadata < Dependencies > ( ComponentConstants . DependencyKey , dependencies , target . constructor ) ;
2465
25- if ( expression ) {
66+ // Store expression if provided
67+ if ( resolvedExpression ) {
2668 const expressions : Expressions =
2769 getOwnTypedMetadata < Expressions > ( ComponentConstants . ExpressionKey , target . constructor ) || { } ;
2870
2971 if ( ! expressions [ propertyKey ] ) {
30- expressions [ propertyKey ] = expression ;
72+ expressions [ propertyKey ] = resolvedExpression ;
3173 }
3274
3375 defineTypedMetadata < Expressions > ( ComponentConstants . ExpressionKey , expressions , target . constructor ) ;
0 commit comments