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.
 | ||||
|     const tmplScope = Scope.forNodes(this.tcb, this.scope, this.template, guard); | ||||
| 
 | ||||
|     // Render the template's `Scope` into a block.
 | ||||
|     let tmplBlock: ts.Statement = ts.createBlock(tmplScope.render()); | ||||
|     // Render the template's `Scope` into its statements.
 | ||||
|     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) { | ||||
|       // The scope has a guard that needs to be applied, so wrap the template block into an `if`
 | ||||
|       // statement containing the guard expression.
 | ||||
|  | ||||
| @ -585,7 +585,7 @@ describe('type check blocks', () => { | ||||
|           type: 'invocation', | ||||
|         }] | ||||
|       }]; | ||||
|       const TEMPLATE = `<div *ngIf="person"></div>`; | ||||
|       const TEMPLATE = `<div *ngIf="person">{{person.name}}</div>`; | ||||
|       const block = tcb(TEMPLATE, DIRECTIVES); | ||||
|       expect(block).toContain('if (NgIf.ngTemplateGuard_ngIf(_t1, ((ctx).person)))'); | ||||
|     }); | ||||
| @ -601,10 +601,26 @@ describe('type check blocks', () => { | ||||
|           type: 'binding', | ||||
|         }] | ||||
|       }]; | ||||
|       const TEMPLATE = `<div *ngIf="person !== null"></div>`; | ||||
|       const TEMPLATE = `<div *ngIf="person !== null">{{person.name}}</div>`; | ||||
|       const block = tcb(TEMPLATE, DIRECTIVES); | ||||
|       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', () => { | ||||
| @ -681,7 +697,7 @@ describe('type check blocks', () => { | ||||
|     }; | ||||
| 
 | ||||
|     describe('config.applyTemplateContextGuards', () => { | ||||
|       const TEMPLATE = `<div *dir></div>`; | ||||
|       const TEMPLATE = `<div *dir>{{ value }}</div>`; | ||||
|       const GUARD_APPLIED = 'if (Dir.ngTemplateContextGuard('; | ||||
| 
 | ||||
|       it('should apply template context guards when enabled', () => { | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user