@@ -68,7 +68,7 @@ runInEachFileSystem(() => {
6868
6969 export class TestCmp {
7070 mySignal1 = signal<number>(0);
71- mySignal2:Signal<number>;
71+ mySignal2: Signal<number>;
7272 }` ,
7373 } ,
7474 ] ) ;
@@ -128,7 +128,7 @@ runInEachFileSystem(() => {
128128 'TestCmp' : `<div>{{ mySignal2 }}</div>` ,
129129 } ,
130130 source : `
131- import {signal, Signal, computed} from '@angular/core';
131+ import {signal, computed} from '@angular/core';
132132
133133 export class TestCmp {
134134 mySignal1 = signal<number>(0);
@@ -275,6 +275,82 @@ runInEachFileSystem(() => {
275275 expect ( getSourceCodeForDiagnostic ( diags [ 0 ] ) ) . toBe ( `myRequiredModel` ) ;
276276 } ) ;
277277
278+ it ( 'should not produce a warning when a signal is not invoked in a banana in box binding' , ( ) => {
279+ const fileName = absoluteFrom ( '/main.ts' ) ;
280+ const { program, templateTypeChecker} = setup ( [
281+ {
282+ fileName,
283+ templates : {
284+ 'TestCmp' : `<div [(value)]="signal">{{ myRequiredModel }}</div>` ,
285+ } ,
286+ source : `
287+ import {signal} from '@angular/core';
288+
289+ export class TestCmp {
290+ mySignal = signal(0);
291+ }` ,
292+ } ,
293+ ] ) ;
294+ const sf = getSourceFileOrError ( program , fileName ) ;
295+ const component = getClass ( sf , 'TestCmp' ) ;
296+ const extendedTemplateChecker = new ExtendedTemplateCheckerImpl (
297+ templateTypeChecker ,
298+ program . getTypeChecker ( ) ,
299+ [ interpolatedSignalFactory ] ,
300+ { } /* options */ ,
301+ ) ;
302+ const diags = extendedTemplateChecker . getDiagnosticsForComponent ( component ) ;
303+ expect ( diags . length ) . toBe ( 0 ) ;
304+ } ) ;
305+
306+ it ( 'should not produce a warning when a signal is not invoked in an input binding as they are skipped' , ( ) => {
307+ const fileName = absoluteFrom ( '/main.ts' ) ;
308+ const { program, templateTypeChecker} = setup ( [
309+ {
310+ fileName,
311+ templates : {
312+ 'TestCmp' : `<div dir [myInput]="mySignal"></div>` ,
313+ } ,
314+ source : `
315+ import {signal, input} from '@angular/core';
316+
317+ export class TestDir {
318+ myInput = input.required();
319+ }
320+ export class TestCmp {
321+ mySignal = signal(0);
322+ }` ,
323+ declarations : [
324+ {
325+ type : 'directive' ,
326+ name : 'TestDir' ,
327+ selector : '[dir]' ,
328+ inputs : {
329+ myInput : {
330+ isSignal : true ,
331+ bindingPropertyName : 'myInput' ,
332+ classPropertyName : 'myInput' ,
333+ required : true ,
334+ transform : null ,
335+ } ,
336+ } ,
337+ } ,
338+ ] ,
339+ } ,
340+ ] ) ;
341+ const sf = getSourceFileOrError ( program , fileName ) ;
342+ const component = getClass ( sf , 'TestCmp' ) ;
343+ const extendedTemplateChecker = new ExtendedTemplateCheckerImpl (
344+ templateTypeChecker ,
345+ program . getTypeChecker ( ) ,
346+ [ interpolatedSignalFactory ] ,
347+ { } ,
348+ /* options */
349+ ) ;
350+ const diags = extendedTemplateChecker . getDiagnosticsForComponent ( component ) ;
351+ expect ( diags . length ) . toBe ( 0 ) ;
352+ } ) ;
353+
278354 it ( 'should produce a warning when a signal in a nested property read is not invoked' , ( ) => {
279355 const fileName = absoluteFrom ( '/main.ts' ) ;
280356 const { program, templateTypeChecker} = setup ( [
@@ -284,7 +360,7 @@ runInEachFileSystem(() => {
284360 'TestCmp' : `<div>{{ obj.nested.prop.signal }}</div>` ,
285361 } ,
286362 source : `
287- import {signal, Signal } from '@angular/core';
363+ import {signal} from '@angular/core';
288364
289365 export class TestCmp {
290366 obj = {
@@ -350,7 +426,7 @@ runInEachFileSystem(() => {
350426 'TestCmp' : `<div>{{ mySignal2() }}</div>` ,
351427 } ,
352428 source : `
353- import {signal, Signal, computed} from '@angular/core';
429+ import {signal, computed} from '@angular/core';
354430
355431 export class TestCmp {
356432 mySignal1 = signal<number>(0);
@@ -549,6 +625,65 @@ runInEachFileSystem(() => {
549625 expect ( getSourceCodeForDiagnostic ( diags [ 0 ] ) ) . toBe ( `myNestedSignal` ) ;
550626 } ) ;
551627
628+ it ( "should produce a warning when signal isn't invoked on dom property binding" , ( ) => {
629+ const fileName = absoluteFrom ( '/main.ts' ) ;
630+ const { program, templateTypeChecker} = setup ( [
631+ {
632+ fileName,
633+ templates : {
634+ 'TestCmp' : `<div [id]="mySignal"></div>` ,
635+ } ,
636+ source : `
637+ import {signal} from '@angular/core';
638+
639+ export class TestCmp {
640+ mySignal = signal<number>(0);
641+ }` ,
642+ } ,
643+ ] ) ;
644+ const sf = getSourceFileOrError ( program , fileName ) ;
645+ const component = getClass ( sf , 'TestCmp' ) ;
646+ const extendedTemplateChecker = new ExtendedTemplateCheckerImpl (
647+ templateTypeChecker ,
648+ program . getTypeChecker ( ) ,
649+ [ interpolatedSignalFactory ] ,
650+ { } /* options */ ,
651+ ) ;
652+ const diags = extendedTemplateChecker . getDiagnosticsForComponent ( component ) ;
653+ expect ( diags . length ) . toBe ( 1 ) ;
654+ expect ( diags [ 0 ] . category ) . toBe ( ts . DiagnosticCategory . Warning ) ;
655+ expect ( diags [ 0 ] . code ) . toBe ( ngErrorCode ( ErrorCode . INTERPOLATED_SIGNAL_NOT_INVOKED ) ) ;
656+ expect ( getSourceCodeForDiagnostic ( diags [ 0 ] ) ) . toBe ( `mySignal` ) ;
657+ } ) ;
658+
659+ it ( 'should not produce a warning when signal is invoked on dom property binding' , ( ) => {
660+ const fileName = absoluteFrom ( '/main.ts' ) ;
661+ const { program, templateTypeChecker} = setup ( [
662+ {
663+ fileName,
664+ templates : {
665+ 'TestCmp' : `<div [id]="mySignal()"></div>` ,
666+ } ,
667+ source : `
668+ import {signal} from '@angular/core';
669+
670+ export class TestCmp {
671+ mySignal = signal<number>(0);
672+ }` ,
673+ } ,
674+ ] ) ;
675+ const sf = getSourceFileOrError ( program , fileName ) ;
676+ const component = getClass ( sf , 'TestCmp' ) ;
677+ const extendedTemplateChecker = new ExtendedTemplateCheckerImpl (
678+ templateTypeChecker ,
679+ program . getTypeChecker ( ) ,
680+ [ interpolatedSignalFactory ] ,
681+ { } /* options */ ,
682+ ) ;
683+ const diags = extendedTemplateChecker . getDiagnosticsForComponent ( component ) ;
684+ expect ( diags . length ) . toBe ( 0 ) ;
685+ } ) ;
686+
552687 it ( 'should not produce a warning with other Signal type' , ( ) => {
553688 const fileName = absoluteFrom ( '/main.ts' ) ;
554689 const { program, templateTypeChecker} = setup ( [
0 commit comments