fix(compiler-cli): incorrectly type checking calls to implicit template variables (#39686)
Currently when we encounter an implicit method call (e.g. `{{ foo(1) }}`) and we manage to resolve its receiver to something within the template, we assume that the method is on the receiver itself so we generate a type checking code to reflect it. This assumption is true in most cases, but it breaks down if the call is on an implicit receiver and the receiver itself is being invoked. E.g. ``` <div *ngFor="let fn of functions">{{ fn(1) }}</div> ``` These changes resolve the issue by generating a regular function call if the method call's receiver is pointing to `$implicit`. Fixes #39634. PR Close #39686
This commit is contained in:
parent
7e724add7e
commit
a61fe96b70
|
@ -1622,7 +1622,7 @@ class TcbExpressionTranslator {
|
|||
return null;
|
||||
}
|
||||
|
||||
const method = ts.createPropertyAccess(wrapForDiagnostics(receiver), ast.name);
|
||||
const method = wrapForDiagnostics(receiver);
|
||||
addParseSpanInfo(method, ast.nameSpan);
|
||||
const args = ast.args.map(arg => this.translate(arg));
|
||||
const node = ts.createCall(method, undefined, args);
|
||||
|
|
|
@ -71,7 +71,7 @@ describe('type check blocks diagnostics', () => {
|
|||
const TEMPLATE = `<ng-template let-method>{{ method(a, b) }}</ng-template>`;
|
||||
expect(tcbWithSpans(TEMPLATE))
|
||||
.toContain(
|
||||
'(_t2 /*27,39*/).method /*27,33*/(((ctx).a /*34,35*/) /*34,35*/, ((ctx).b /*37,38*/) /*37,38*/) /*27,39*/');
|
||||
'(_t2 /*27,39*/) /*27,33*/(((ctx).a /*34,35*/) /*34,35*/, ((ctx).b /*37,38*/) /*37,38*/) /*27,39*/');
|
||||
});
|
||||
|
||||
it('should annotate function calls', () => {
|
||||
|
|
|
@ -106,7 +106,7 @@ describe('type check blocks', () => {
|
|||
it('should handle method calls of template variables', () => {
|
||||
const TEMPLATE = `<ng-template let-a>{{a(1)}}</ng-template>`;
|
||||
expect(tcb(TEMPLATE)).toContain('var _t2 = _t1.$implicit;');
|
||||
expect(tcb(TEMPLATE)).toContain('(_t2).a(1)');
|
||||
expect(tcb(TEMPLATE)).toContain('(_t2)(1)');
|
||||
});
|
||||
|
||||
it('should handle implicit vars when using microsyntax', () => {
|
||||
|
@ -114,6 +114,12 @@ describe('type check blocks', () => {
|
|||
expect(tcb(TEMPLATE)).toContain('var _t2 = _t1.$implicit;');
|
||||
});
|
||||
|
||||
it('should handle direct calls of an implicit template variable', () => {
|
||||
const TEMPLATE = `<div *ngFor="let a of letters">{{a(1)}}</div>`;
|
||||
expect(tcb(TEMPLATE)).toContain('var _t2 = _t1.$implicit;');
|
||||
expect(tcb(TEMPLATE)).toContain('(_t2)(1)');
|
||||
});
|
||||
|
||||
describe('type constructors', () => {
|
||||
it('should handle missing property bindings', () => {
|
||||
const TEMPLATE = `<div dir [inputA]="foo"></div>`;
|
||||
|
|
|
@ -1009,6 +1009,30 @@ export declare class AnimationEvent {
|
|||
expect(diags.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should allow the implicit value of an NgFor to be invoked', () => {
|
||||
env.tsconfig({fullTemplateTypeCheck: true, strictInputTypes: true});
|
||||
env.write('test.ts', `
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, NgModule} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<div *ngFor="let fn of functions">{{fn()}}</div>',
|
||||
})
|
||||
class TestCmp {
|
||||
functions = [() => 1, () => 2];
|
||||
}
|
||||
|
||||
@NgModule({
|
||||
declarations: [TestCmp],
|
||||
imports: [CommonModule],
|
||||
})
|
||||
class Module {}
|
||||
`);
|
||||
|
||||
env.driveMain();
|
||||
});
|
||||
|
||||
it('should infer the context of NgIf', () => {
|
||||
env.tsconfig({strictTemplates: true});
|
||||
env.write('test.ts', `
|
||||
|
|
Loading…
Reference in New Issue