@@ -15,6 +15,10 @@ type DeepConfig = { deep: { config: number } };
1515declare const testConfig : TestConfig ;
1616declare const testValue : TestValue ;
1717declare const TestAdapter : WireAdapterConstructor < TestConfig , TestValue , TestContext > ;
18+ declare const TestAdapterWithImperative : {
19+ ( config : TestConfig ) : TestValue ;
20+ adapter : WireAdapterConstructor < TestConfig , TestValue , TestContext > ;
21+ } ;
1822declare const AnyAdapter : any ;
1923declare const InvalidAdapter : object ;
2024declare const DeepConfigAdapter : WireAdapterConstructor < DeepConfig , TestValue > ;
@@ -39,7 +43,7 @@ export class PropertyDecorators extends LightningElement {
3943 // Valid - basic
4044 @wire ( TestAdapter , { config : 'config' } )
4145 basic ?: TestValue ;
42- @wire ( TestAdapter , { config : '$config ' } )
46+ @wire ( TestAdapter , { config : '$configProp ' } )
4347 simpleReactive ?: TestValue ;
4448 @wire ( TestAdapter , { config : '$nested.prop' } )
4549 nestedReactive ?: TestValue ;
@@ -126,6 +130,72 @@ export class PropertyDecorators extends LightningElement {
126130 never ?: never ;
127131}
128132
133+ /** Validations for decorated properties/fields */
134+ export class PropertyDecoratorsWithImperative extends LightningElement {
135+ // Helper props
136+ configProp = 'config' as const ;
137+ nested = { prop : 'config' , invalid : 123 } as const ;
138+ // 'nested.prop' is not directly used, but helps validate that the reactive config resolution
139+ // uses the object above, rather than a weird prop name
140+ 'nested.prop' = false ;
141+ number = 123 ;
142+ // --- VALID --- //
143+ // Valid - basic
144+ @wire ( TestAdapterWithImperative , { config : 'config' } )
145+ basic ?: TestValue ;
146+ @wire ( TestAdapterWithImperative , { config : '$configProp' } )
147+ simpleReactive ?: TestValue ;
148+ @wire ( TestAdapterWithImperative , { config : '$nested.prop' } )
149+ nestedReactive ?: TestValue ;
150+ // Valid - as const
151+ @wire ( TestAdapterWithImperative , { config : 'config' } as const )
152+ basicAsConst ?: TestValue ;
153+ @wire ( TestAdapterWithImperative , { config : '$configProp' } as const )
154+ simpleReactiveAsConst ?: TestValue ;
155+ // Valid - using `any`
156+ @wire ( TestAdapterWithImperative , { } as any )
157+ configAsAny ?: TestValue ;
158+ @wire ( TestAdapterWithImperative , { config : 'config' } )
159+ propAsAny ?: any ;
160+ // Valid - prop assignment
161+ @wire ( TestAdapterWithImperative , { config : 'config' } )
162+ nonNullAssertion ! : TestValue ;
163+ @wire ( TestAdapterWithImperative , { config : 'config' } )
164+ explicitDefaultType : TestValue = testValue ;
165+ @wire ( TestAdapterWithImperative , { config : 'config' } )
166+ implicitDefaultType = testValue ;
167+
168+ // --- INVALID --- //
169+ // @ts -expect-error Too many wire parameters
170+ @wire ( TestAdapterWithImperative , { config : 'config' } , { } )
171+ tooManyWireParams ?: TestValue ;
172+ // @ts -expect-error Bad config type
173+ @wire ( TestAdapterWithImperative , { bad : 'value' } )
174+ badConfig ?: TestValue ;
175+ // @ts -expect-error Bad prop type
176+ @wire ( TestAdapterWithImperative , { config : 'config' } )
177+ badPropType ?: { bad : 'value' } ;
178+ // @ts -expect-error Referenced reactive prop does not exist
179+ @wire ( TestAdapterWithImperative , { config : '$nonexistentProp' } as const )
180+ nonExistentReactiveProp ?: TestValue ;
181+
182+ // --- AMBIGUOUS --- //
183+ // Passing a config is optional because adapters don't strictly need to use it.
184+ // Can we be smarter about the type and require a config, but only if the adapter does?
185+ @wire ( TestAdapterWithImperative )
186+ noConfig ?: TestValue ;
187+ // Because the basic type `string` could be _any_ string, we can't narrow it and compare against
188+ // the component's props, so we must accept all string props, even if they're incorrect.
189+ // We could technically be strict, and enforce that all configs objects use `as const`, but very
190+ // few projects currently use it (there is no need) and the error reported is not simple to
191+ // understand.
192+ @wire ( TestAdapterWithImperative , { config : 'incorrect' } )
193+ wrongConfigButInferredAsString ?: TestValue ;
194+ // People shouldn't do this, and they probably never (heh) will. TypeScript allows it, though.
195+ @wire ( TestAdapterWithImperative , { config : 'config' } )
196+ never ?: never ;
197+ }
198+
129199/** Validations for decorated methods */
130200export class MethodDecorators extends LightningElement {
131201 // Helper props
@@ -141,13 +211,13 @@ export class MethodDecorators extends LightningElement {
141211 basic ( _ : TestValue ) { }
142212 @wire ( TestAdapter , { config : 'config' } )
143213 async asyncMethod ( _ : TestValue ) { }
144- @wire ( TestAdapter , { config : '$config ' } )
214+ @wire ( TestAdapter , { config : '$configProp ' } )
145215 simpleReactive ( _ : TestValue ) { }
146216 @wire ( TestAdapter , { config : '$nested.prop' } )
147217 nestedReactive ( _ : TestValue ) { }
148- @wire ( TestAdapter , { config : '$config ' } )
218+ @wire ( TestAdapter , { config : '$configProp ' } )
149219 optionalParam ( _ ?: TestValue ) { }
150- @wire ( TestAdapter , { config : '$config ' } )
220+ @wire ( TestAdapter , { config : '$configProp ' } )
151221 noParam ( ) { }
152222 // Valid - as const
153223 @wire ( TestAdapter , { config : 'config' } as const )
@@ -222,6 +292,67 @@ export class MethodDecorators extends LightningElement {
222292 implicitDefaultType ( _ = testValue ) { }
223293}
224294
295+ /** Validations for decorated methods */
296+ export class MethodDecoratorsWithImperative extends LightningElement {
297+ // Helper props
298+ configProp = 'config' as const ;
299+ nested = { prop : 'config' , invalid : 123 } as const ;
300+ // 'nested.prop' is not directly used, but helps validate that the reactive config resolution
301+ // uses the object above, rather than a weird prop name
302+ 'nested.prop' = false ;
303+ number = 123 ;
304+ // --- VALID --- //
305+ // Valid - basic
306+ @wire ( TestAdapterWithImperative , { config : 'config' } )
307+ basic ( _ : TestValue ) { }
308+ @wire ( TestAdapterWithImperative , { config : 'config' } )
309+ async asyncMethod ( _ : TestValue ) { }
310+ @wire ( TestAdapterWithImperative , { config : '$configProp' } )
311+ simpleReactive ( _ : TestValue ) { }
312+ @wire ( TestAdapterWithImperative , { config : '$nested.prop' } )
313+ nestedReactive ( _ : TestValue ) { }
314+ @wire ( TestAdapterWithImperative , { config : '$configProp' } )
315+ optionalParam ( _ ?: TestValue ) { }
316+ @wire ( TestAdapterWithImperative , { config : '$configProp' } )
317+ noParam ( ) { }
318+ // Valid - as const
319+ @wire ( TestAdapterWithImperative , { config : 'config' } as const )
320+ basicAsConst ( _ : TestValue ) { }
321+ @wire ( TestAdapterWithImperative , { config : '$configProp' } as const )
322+ simpleReactiveAsConst ( _ : TestValue ) { }
323+ @wire ( TestAdapterWithImperative , { config : '$nested.prop' } as const )
324+ nestedReactiveAsConst ( _ : TestValue ) { }
325+ // Valid - using `any`
326+ @wire ( TestAdapterWithImperative , { } as any )
327+ configAsAny ( _ : TestValue ) { }
328+ @wire ( TestAdapterWithImperative , { config : 'config' } )
329+ paramAsAny ( _ : any ) { }
330+
331+ // --- INVALID --- //
332+ // @ts -expect-error Too many wire parameters
333+ @wire ( TestAdapterWithImperative , { config : 'config' } , { } )
334+ tooManyWireParams ( _ : TestValue ) { }
335+ // @ts -expect-error Too many method parameters
336+ @wire ( TestAdapterWithImperative , { config : 'config' } )
337+ tooManyParameters ( _a : TestValue , _b : TestValue ) { }
338+
339+ // --- AMBIGUOUS --- //
340+ // Passing a config is optional because adapters don't strictly need to use it.
341+ // Can we be smarter about the type and require a config, but only if the adapter does?
342+ @wire ( TestAdapterWithImperative )
343+ noConfig ( _ : TestValue ) : void { }
344+ // Because the basic type `string` could be _any_ string, we can't narrow it and compare against
345+ // the component's props, so we must accept all string props, even if they're incorrect.
346+ // We could technically be strict, and enforce that all configs objects use `as const`, but very
347+ // few projects currently use it (there is no need) and the error reported is not simple to
348+ // understand.
349+ @wire ( TestAdapterWithImperative , { config : 'incorrect' } )
350+ wrongConfigButInferredAsString ( _ : TestValue ) : void { }
351+ // Wire adapters shouldn't use default params, but the type system doesn't know the difference
352+ @wire ( TestAdapterWithImperative , { config : 'config' } )
353+ implicitDefaultType ( _ = testValue ) { }
354+ }
355+
225356/** Validations for decorated getters */
226357export class GetterDecorators extends LightningElement {
227358 // Helper props
@@ -244,7 +375,7 @@ export class GetterDecorators extends LightningElement {
244375 // we must return something. Since we don't have any data to return, we return `undefined`
245376 return undefined ;
246377 }
247- @wire ( TestAdapter , { config : '$config ' } )
378+ @wire ( TestAdapter , { config : '$configProp ' } )
248379 get simpleReactive ( ) {
249380 return testValue ;
250381 }
@@ -341,6 +472,69 @@ export class GetterDecorators extends LightningElement {
341472 }
342473}
343474
475+ /** Validations for decorated getters */
476+ export class GetterDecoratorsWithImperative extends LightningElement {
477+ // Helper props
478+ configProp = 'config' as const ;
479+ nested = { prop : 'config' , invalid : 123 } as const ;
480+ // 'nested.prop' is not directly used, but helps validate that the reactive config resolution
481+ // uses the object above, rather than a weird prop name
482+ 'nested.prop' = false ;
483+ number = 123 ;
484+ // --- VALID --- //
485+
486+ // Valid - basic
487+ @wire ( TestAdapterWithImperative , { config : 'config' } )
488+ get basic ( ) {
489+ return testValue ;
490+ }
491+ @wire ( TestAdapterWithImperative , { config : 'config' } )
492+ get undefined ( ) {
493+ // The function implementation of a wired getter is ignored, but TypeScript enforces that
494+ // we must return something. Since we don't have any data to return, we return `undefined`
495+ return undefined ;
496+ }
497+ @wire ( TestAdapterWithImperative , { config : '$configProp' } )
498+ get simpleReactive ( ) {
499+ return testValue ;
500+ }
501+ @wire ( TestAdapterWithImperative , { config : '$nested.prop' } )
502+ get nestedReactive ( ) {
503+ return testValue ;
504+ }
505+ // Valid - using `any`
506+ @wire ( TestAdapterWithImperative , { } as any )
507+ get configAsAny ( ) {
508+ return testValue ;
509+ }
510+ @wire ( TestAdapterWithImperative , { config : 'config' } )
511+ get valueAsAny ( ) {
512+ return null as any ;
513+ }
514+
515+ // --- INVALID --- //
516+ // @ts -expect-error Too many wire parameters
517+ @wire ( TestAdapterWithImperative , { config : 'config' } , { } )
518+ get tooManyWireParams ( ) {
519+ return testValue ;
520+ }
521+ // @ts -expect-error Bad config type
522+ @wire ( TestAdapterWithImperative , { bad : 'value' } )
523+ get badConfig ( ) {
524+ return testValue ;
525+ }
526+ // @ts -expect-error Bad value type
527+ @wire ( TestAdapterWithImperative , { config : 'config' } )
528+ get badValueType ( ) {
529+ return { bad : 'value' } ;
530+ }
531+ // @ts -expect-error Referenced reactive prop does not exist
532+ @wire ( TestAdapterWithImperative , { config : '$nonexistentProp' } as const )
533+ get nonExistentReactiveProp ( ) {
534+ return testValue ;
535+ }
536+ }
537+
344538/** Validations for decorated setters */
345539export class Setter extends LightningElement {
346540 // Helper props
@@ -355,7 +549,7 @@ export class Setter extends LightningElement {
355549 // Valid - basic
356550 @wire ( TestAdapter , { config : 'config' } )
357551 set basic ( _ : TestValue ) { }
358- @wire ( TestAdapter , { config : '$config ' } )
552+ @wire ( TestAdapter , { config : '$configProp ' } )
359553 set simpleReactive ( _ : TestValue ) { }
360554 @wire ( TestAdapter , { config : '$nested.prop' } )
361555 set nestedReactive ( _ : TestValue ) { }
@@ -411,3 +605,61 @@ export class Setter extends LightningElement {
411605 @wire ( DeepConfigAdapter , { deep : { config : '$number' } } as const )
412606 set deepReactive ( _ : TestValue ) { }
413607}
608+
609+ /** Validations for decorated setters */
610+ export class SetterWithImperative extends LightningElement {
611+ // Helper props
612+ configProp = 'config' as const ;
613+ nested = { prop : 'config' , invalid : 123 } as const ;
614+ // 'nested.prop' is not directly used, but helps validate that the reactive config resolution
615+ // uses the object above, rather than a weird prop name
616+ 'nested.prop' = false ;
617+ number = 123 ;
618+ // --- VALID --- //
619+
620+ // Valid - basic
621+ @wire ( TestAdapterWithImperative , { config : 'config' } )
622+ set basic ( _ : TestValue ) { }
623+ @wire ( TestAdapterWithImperative , { config : '$configProp' } )
624+ set simpleReactive ( _ : TestValue ) { }
625+ @wire ( TestAdapterWithImperative , { config : '$nested.prop' } )
626+ set nestedReactive ( _ : TestValue ) { }
627+ // Valid - as const
628+ @wire ( TestAdapterWithImperative , { config : 'config' } as const )
629+ set basicAsConst ( _ : TestValue ) { }
630+ @wire ( TestAdapterWithImperative , { config : '$configProp' } as const )
631+ set simpleReactiveAsConst ( _ : TestValue ) { }
632+ @wire ( TestAdapterWithImperative , { config : '$nested.prop' } as const )
633+ set nestedReactiveAsConst ( _ : TestValue ) { }
634+ // Valid - using `any`
635+ @wire ( TestAdapterWithImperative , { } as any )
636+ set configAsAny ( _ : TestValue ) { }
637+ @wire ( TestAdapterWithImperative , { config : 'config' } )
638+ set valueAsAny ( _ : any ) { }
639+
640+ // --- INVALID --- //
641+ // @ts -expect-error Too many wire parameters
642+ @wire ( TestAdapterWithImperative , { config : 'config' } , { } )
643+ set tooManyWireParams ( _ : TestValue ) { }
644+ // @ts -expect-error Bad config type
645+ @wire ( TestAdapterWithImperative , { bad : 'value' } )
646+ set badConfig ( _ : TestValue ) { }
647+ // @ts -expect-error Bad value type
648+ @wire ( TestAdapterWithImperative , { config : 'config' } )
649+ set badValueType ( _ : { bad : 'value' } ) { }
650+ // @ts -expect-error Referenced reactive prop does not exist
651+ @wire ( TestAdapterWithImperative , { config : '$nonexistentProp' } as const )
652+ set nonExistentReactiveProp ( _ : TestValue ) { }
653+ // @ts -expect-error Referenced reactive prop is the wrong type
654+ @wire ( TestAdapterWithImperative , { config : '$number' } as const )
655+ set numberReactiveProp ( _ : TestValue ) { }
656+ // @ts -expect-error Referenced nested reactive prop does not exist
657+ @wire ( TestAdapterWithImperative , { config : '$nested.nonexistent' } as const )
658+ set nonexistentNestedReactiveProp ( _ : TestValue ) { }
659+ // @ts -expect-error Referenced nested reactive prop does not exist
660+ @wire ( TestAdapterWithImperative , { config : '$nested.invalid' } as const )
661+ set invalidNestedReactiveProp ( _ : TestValue ) { }
662+ // @ts -expect-error Incorrect non-reactive string literal type
663+ @wire ( TestAdapterWithImperative , { config : 'not reactive' } as const )
664+ set nonReactiveStringLiteral ( _ : TestValue ) { }
665+ }
0 commit comments