perf(compiler-cli): don't emit template guards when child scope is empty (#38418)
For a template that contains for example `<span *ngIf="first"></span>` there's no need to render the `NgIf` guard expression, as the child scope does not have any type-checking statements, so any narrowing effect of the guard is not applicable. This seems like a minor improvement, however it reduces the number of flow-node antecedents that TypeScript needs to keep into account for such cases, resulting in an overall reduction of type-checking time. PR Close #38418
This commit is contained in:
parent
fb8f4b4d72
commit
1388c1761f
|
@ -304,8 +304,19 @@ class TcbTemplateBodyOp extends TcbOp {
|
||||||
// children, as well as tracks bindings within the template.
|
// children, as well as tracks bindings within the template.
|
||||||
const tmplScope = Scope.forNodes(this.tcb, this.scope, this.template, guard);
|
const tmplScope = Scope.forNodes(this.tcb, this.scope, this.template, guard);
|
||||||
|
|
||||||
// Render the template's `Scope` into a block.
|
// Render the template's `Scope` into its statements.
|
||||||
let tmplBlock: ts.Statement = ts.createBlock(tmplScope.render());
|
const statements = tmplScope.render();
|
||||||
|
if (statements.length === 0) {
|
||||||
|
// As an optimization, don't generate the scope's block if it has no statements. This is
|
||||||
|
// beneficial for templates that contain for example `<span *ngIf="first"></span>`, in which
|
||||||
|
// case there's no need to render the `NgIf` guard expression. This seems like a minor
|
||||||
|
// improvement, however it reduces the number of flow-node antecedents that TypeScript needs
|
||||||
|
// to keep into account for such cases, resulting in an overall reduction of
|
||||||
|
// type-checking time.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let tmplBlock: ts.Statement = ts.createBlock(statements);
|
||||||
if (guard !== null) {
|
if (guard !== null) {
|
||||||
// The scope has a guard that needs to be applied, so wrap the template block into an `if`
|
// The scope has a guard that needs to be applied, so wrap the template block into an `if`
|
||||||
// statement containing the guard expression.
|
// statement containing the guard expression.
|
||||||
|
|
|
@ -585,7 +585,7 @@ describe('type check blocks', () => {
|
||||||
type: 'invocation',
|
type: 'invocation',
|
||||||
}]
|
}]
|
||||||
}];
|
}];
|
||||||
const TEMPLATE = `<div *ngIf="person"></div>`;
|
const TEMPLATE = `<div *ngIf="person">{{person.name}}</div>`;
|
||||||
const block = tcb(TEMPLATE, DIRECTIVES);
|
const block = tcb(TEMPLATE, DIRECTIVES);
|
||||||
expect(block).toContain('if (NgIf.ngTemplateGuard_ngIf(_t1, ((ctx).person)))');
|
expect(block).toContain('if (NgIf.ngTemplateGuard_ngIf(_t1, ((ctx).person)))');
|
||||||
});
|
});
|
||||||
|
@ -601,10 +601,26 @@ describe('type check blocks', () => {
|
||||||
type: 'binding',
|
type: 'binding',
|
||||||
}]
|
}]
|
||||||
}];
|
}];
|
||||||
const TEMPLATE = `<div *ngIf="person !== null"></div>`;
|
const TEMPLATE = `<div *ngIf="person !== null">{{person.name}}</div>`;
|
||||||
const block = tcb(TEMPLATE, DIRECTIVES);
|
const block = tcb(TEMPLATE, DIRECTIVES);
|
||||||
expect(block).toContain('if ((((ctx).person)) !== (null))');
|
expect(block).toContain('if ((((ctx).person)) !== (null))');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not emit guards when the child scope is empty', () => {
|
||||||
|
const DIRECTIVES: TestDeclaration[] = [{
|
||||||
|
type: 'directive',
|
||||||
|
name: 'NgIf',
|
||||||
|
selector: '[ngIf]',
|
||||||
|
inputs: {'ngIf': 'ngIf'},
|
||||||
|
ngTemplateGuards: [{
|
||||||
|
inputName: 'ngIf',
|
||||||
|
type: 'invocation',
|
||||||
|
}]
|
||||||
|
}];
|
||||||
|
const TEMPLATE = `<div *ngIf="person">static</div>`;
|
||||||
|
const block = tcb(TEMPLATE, DIRECTIVES);
|
||||||
|
expect(block).not.toContain('NgIf.ngTemplateGuard_ngIf');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('outputs', () => {
|
describe('outputs', () => {
|
||||||
|
@ -681,7 +697,7 @@ describe('type check blocks', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('config.applyTemplateContextGuards', () => {
|
describe('config.applyTemplateContextGuards', () => {
|
||||||
const TEMPLATE = `<div *dir></div>`;
|
const TEMPLATE = `<div *dir>{{ value }}</div>`;
|
||||||
const GUARD_APPLIED = 'if (Dir.ngTemplateContextGuard(';
|
const GUARD_APPLIED = 'if (Dir.ngTemplateContextGuard(';
|
||||||
|
|
||||||
it('should apply template context guards when enabled', () => {
|
it('should apply template context guards when enabled', () => {
|
||||||
|
|
Loading…
Reference in New Issue