66 * found in the LICENSE file at https://angular.io/license
77 */
88
9- import { AST , ASTWithSource , BindingPipe , ParseSourceSpan , PropertyRead , PropertyWrite , R3Identifiers , SafePropertyRead , TmplAstBoundAttribute , TmplAstBoundEvent , TmplAstElement , TmplAstNode , TmplAstReference , TmplAstTemplate , TmplAstTextAttribute , TmplAstVariable } from '@angular/compiler' ;
9+ import { AST , ASTWithSource , BindingPipe , BindingType , ParseSourceSpan , PropertyRead , PropertyWrite , R3Identifiers , SafePropertyRead , TmplAstBoundAttribute , TmplAstBoundEvent , TmplAstElement , TmplAstNode , TmplAstReference , TmplAstTemplate , TmplAstTextAttribute , TmplAstVariable } from '@angular/compiler' ;
1010import ts from 'typescript' ;
1111
1212import { AbsoluteFsPath } from '../../file_system' ;
@@ -350,15 +350,32 @@ export class SymbolBuilder {
350350 return host !== null ? { kind : SymbolKind . DomBinding , host} : null ;
351351 }
352352
353+ const isTwoWayBinding =
354+ binding instanceof TmplAstBoundAttribute && binding . type === BindingType . TwoWay ;
353355 const nodes = findAllMatchingNodes (
354356 this . typeCheckBlock , { withSpan : binding . sourceSpan , filter : isAssignment } ) ;
355357 const bindings : BindingSymbol [ ] = [ ] ;
356358 for ( const node of nodes ) {
357- if ( ! isAccessExpression ( node . left ) ) {
359+ let assignment : ts . PropertyAccessExpression | ts . ElementAccessExpression | null = null ;
360+
361+ // One-way bindings usually are in the form of `dir.input = expression`.
362+ if ( isAccessExpression ( node . left ) ) {
363+ assignment = node . left ;
364+ } else if (
365+ // The property side of two-way bindings is in the
366+ // form of `(dir.input as unknown as someType) = expression`.
367+ isTwoWayBinding && ts . isParenthesizedExpression ( node . left ) &&
368+ ts . isAsExpression ( node . left . expression ) &&
369+ ts . isAsExpression ( node . left . expression . expression ) &&
370+ isAccessExpression ( node . left . expression . expression . expression ) ) {
371+ assignment = node . left . expression . expression . expression ;
372+ }
373+
374+ if ( assignment === null ) {
358375 continue ;
359376 }
360377
361- const signalInputAssignment = unwrapSignalInputWriteTAccessor ( node . left ) ;
378+ const signalInputAssignment = unwrapSignalInputWriteTAccessor ( assignment ) ;
362379 let symbolInfo : TsNodeSymbolInfo | null = null ;
363380
364381 // Signal inputs need special treatment because they are generated with an extra keyed
@@ -376,15 +393,15 @@ export class SymbolBuilder {
376393 tsType : typeSymbol . tsType ,
377394 } ;
378395 } else {
379- symbolInfo = this . getSymbolOfTsNode ( node . left ) ;
396+ symbolInfo = this . getSymbolOfTsNode ( assignment ) ;
380397 }
381398
382399 if ( symbolInfo === null || symbolInfo . tsSymbol === null ) {
383400 continue ;
384401 }
385402
386403 const target = this . getDirectiveSymbolForAccessExpression (
387- signalInputAssignment ?. fieldExpr ?? node . left , consumer ) ;
404+ signalInputAssignment ?. fieldExpr ?? assignment , consumer ) ;
388405 if ( target === null ) {
389406 continue ;
390407 }
0 commit comments