diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts index 2cd6d2c746..00a1b32988 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/template_symbol_builder.ts @@ -18,7 +18,6 @@ import {DirectiveSymbol, DomBindingSymbol, ElementSymbol, ExpressionSymbol, Inpu import {ExpressionIdentifier, findAllMatchingNodes, findFirstMatchingNode, hasExpressionIdentifier} from './comments'; import {TemplateData} from './context'; import {isAccessExpression} from './ts_util'; -import {TcbDirectiveOutputsOp} from './type_check_block'; /** * Generates and caches `Symbol`s for various template structures for a given component. @@ -169,9 +168,9 @@ export class SymbolBuilder { // Outputs are a `ts.CallExpression` that look like one of the two: // * _outputHelper(_t1["outputField"]).subscribe(handler); // * _t1.addEventListener(handler); - const node = findFirstMatchingNode( - this.typeCheckBlock, {withSpan: eventBinding.sourceSpan, filter: ts.isCallExpression}); - if (node === null) { + const outputFieldAccess = findFirstMatchingNode( + this.typeCheckBlock, {withSpan: eventBinding.keySpan, filter: isAccessExpression}); + if (outputFieldAccess === null) { return null; } @@ -181,12 +180,12 @@ export class SymbolBuilder { } if (consumer instanceof TmplAstTemplate || consumer instanceof TmplAstElement) { - if (!ts.isPropertyAccessExpression(node.expression) || - node.expression.name.text !== 'addEventListener') { + if (!ts.isPropertyAccessExpression(outputFieldAccess) || + outputFieldAccess.name.text !== 'addEventListener') { return null; } - const addEventListener = node.expression.name; + const addEventListener = outputFieldAccess.name; const tsSymbol = this.getTypeChecker().getSymbolAtLocation(addEventListener); const tsType = this.getTypeChecker().getTypeAtLocation(addEventListener); const positionInShimFile = this.getShimPositionForNode(addEventListener); @@ -207,11 +206,9 @@ export class SymbolBuilder { }], }; } else { - const outputFieldAccess = TcbDirectiveOutputsOp.decodeOutputCallExpression(node); - if (outputFieldAccess === null) { + if (!ts.isElementAccessExpression(outputFieldAccess)) { return null; } - const tsSymbol = this.getTypeChecker().getSymbolAtLocation(outputFieldAccess.argumentExpression); if (tsSymbol === undefined) { @@ -225,7 +222,7 @@ export class SymbolBuilder { } const positionInShimFile = this.getShimPositionForNode(outputFieldAccess); - const tsType = this.getTypeChecker().getTypeAtLocation(node); + const tsType = this.getTypeChecker().getTypeAtLocation(outputFieldAccess); return { kind: SymbolKind.Output, bindings: [{ diff --git a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts index 9ad75894a9..55713439b9 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts @@ -902,39 +902,6 @@ export class TcbDirectiveOutputsOp extends TcbOp { return null; } - - /** - * Outputs are a `ts.CallExpression` that look like one of the two: - * - `_outputHelper(_t1["outputField"]).subscribe(handler);` - * - `_t1.addEventListener(handler);` - * This method reverses the operations to create a call expression for a directive output. - * It unpacks the given call expression and returns the original element access (i.e. - * `_t1["outputField"]` in the example above). Returns `null` if the given call expression is not - * the expected structure of an output binding - */ - static decodeOutputCallExpression(node: ts.CallExpression): ts.ElementAccessExpression|null { - // `node.expression` === `_outputHelper(_t1["outputField"]).subscribe` or `_t1.addEventListener` - if (!ts.isPropertyAccessExpression(node.expression) || - node.expression.name.text === 'addEventListener') { - // `addEventListener` outputs do not have an `ElementAccessExpression` for the output field. - return null; - } - - if (!ts.isCallExpression(node.expression.expression)) { - return null; - } - - // `node.expression.expression` === `_outputHelper(_t1["outputField"])` - if (node.expression.expression.arguments.length === 0) { - return null; - } - - const [outputFieldAccess] = node.expression.expression.arguments; - if (!ts.isElementAccessExpression(outputFieldAccess)) { - return null; - } - return outputFieldAccess; - } } /** @@ -984,8 +951,10 @@ class TcbUnclaimedOutputsOp extends TcbOp { if (elId === null) { elId = this.scope.resolve(this.element); } + const propertyAccess = ts.createPropertyAccess(elId, 'addEventListener'); + addParseSpanInfo(propertyAccess, output.keySpan); const call = ts.createCall( - /* expression */ ts.createPropertyAccess(elId, 'addEventListener'), + /* expression */ propertyAccess, /* typeArguments */ undefined, /* arguments */[ts.createStringLiteral(output.name), handler]); addParseSpanInfo(call, output.sourceSpan);