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 fa46e7f1db..ab21246052 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
@@ -99,6 +99,13 @@ export function generateTypeCheckBlock(
* `ts.Expression` which can be used to reference the operation's result.
*/
abstract class TcbOp {
+ /**
+ * Set to true if this operation can be considered optional. Optional operations are only executed
+ * when depended upon by other operations, otherwise they are disregarded. This allows for less
+ * code to generate, parse and type-check, overall positively contributing to performance.
+ */
+ abstract readonly optional: boolean;
+
abstract execute(): ts.Expression|null;
/**
@@ -125,6 +132,13 @@ class TcbElementOp extends TcbOp {
super();
}
+ get optional() {
+ // The statement generated by this operation is only used for type-inference of the DOM
+ // element's type and won't report diagnostics by itself, so the operation is marked as optional
+ // to avoid generating statements for DOM elements that are never referenced.
+ return true;
+ }
+
execute(): ts.Identifier {
const id = this.tcb.allocateId();
// Add the declaration of the element using document.createElement.
@@ -148,6 +162,10 @@ class TcbVariableOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): ts.Identifier {
// Look for a context variable for the template.
const ctx = this.scope.resolve(this.template);
@@ -176,6 +194,10 @@ class TcbTemplateContextOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): ts.Identifier {
// Allocate a template ctx variable and declare it with an 'any' type. The type of this variable
// may be narrowed as a result of template guard conditions.
@@ -198,6 +220,10 @@ class TcbTemplateBodyOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): null {
// An `if` will be constructed, within which the template's children will be type checked. The
// `if` is used for two reasons: it creates a new syntactic scope, isolating variables declared
@@ -301,6 +327,10 @@ class TcbTextInterpolationOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): null {
const expr = tcbExpression(this.binding.value, this.tcb, this.scope);
this.scope.addStatement(ts.createExpressionStatement(expr));
@@ -324,6 +354,10 @@ class TcbDirectiveTypeOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): ts.Identifier {
const id = this.tcb.allocateId();
@@ -352,6 +386,10 @@ class TcbDirectiveCtorOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): ts.Identifier {
const id = this.tcb.allocateId();
@@ -409,6 +447,10 @@ class TcbDirectiveInputsOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): null {
const dirId = this.scope.resolve(this.node, this.dir);
@@ -514,6 +556,10 @@ class TcbDirectiveCtorCircularFallbackOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): ts.Identifier {
const id = this.tcb.allocateId();
const typeCtor = this.tcb.env.typeCtorFor(this.dir);
@@ -541,6 +587,10 @@ class TcbDomSchemaCheckerOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): ts.Expression|null {
if (this.checkElement) {
this.tcb.domSchemaChecker.checkElement(this.tcb.id, this.element, this.tcb.schemas);
@@ -597,10 +647,14 @@ class TcbUnclaimedInputsOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): null {
// `this.inputs` contains only those bindings not matched by any directive. These bindings go to
// the element itself.
- const elId = this.scope.resolve(this.element);
+ let elId: ts.Expression|null = null;
// TODO(alxhub): this could be more efficient.
for (const binding of this.element.inputs) {
@@ -622,6 +676,9 @@ class TcbUnclaimedInputsOp extends TcbOp {
if (this.tcb.env.config.checkTypeOfDomBindings && binding.type === BindingType.Property) {
if (binding.name !== 'style' && binding.name !== 'class') {
+ if (elId === null) {
+ elId = this.scope.resolve(this.element);
+ }
// A direct binding to a property.
const propertyName = ATTR_TO_PROP[binding.name] || binding.name;
const prop = ts.createElementAccess(elId, ts.createStringLiteral(propertyName));
@@ -656,6 +713,10 @@ class TcbDirectiveOutputsOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): null {
const dirId = this.scope.resolve(this.node, this.dir);
@@ -723,8 +784,12 @@ class TcbUnclaimedOutputsOp extends TcbOp {
super();
}
+ get optional() {
+ return false;
+ }
+
execute(): null {
- const elId = this.scope.resolve(this.element);
+ let elId: ts.Expression|null = null;
// TODO(alxhub): this could be more efficient.
for (const output of this.element.outputs) {
@@ -749,6 +814,9 @@ class TcbUnclaimedOutputsOp extends TcbOp {
// base `Event` type.
const handler = tcbCreateEventHandler(output, this.tcb, this.scope, EventParamType.Infer);
+ if (elId === null) {
+ elId = this.scope.resolve(this.element);
+ }
const call = ts.createCall(
/* expression */ ts.createPropertyAccess(elId, 'addEventListener'),
/* typeArguments */ undefined,
@@ -965,7 +1033,7 @@ class Scope {
*/
render(): ts.Statement[] {
for (let i = 0; i < this.opQueue.length; i++) {
- this.executeOp(i);
+ this.executeOp(i, /* skipOptional */ true);
}
return this.statements;
}
@@ -1031,7 +1099,7 @@ class Scope {
* Like `executeOp`, but assert that the operation actually returned `ts.Expression`.
*/
private resolveOp(opIndex: number): ts.Expression {
- const res = this.executeOp(opIndex);
+ const res = this.executeOp(opIndex, /* skipOptional */ false);
if (res === null) {
throw new Error(`Error resolving operation, got null`);
}
@@ -1045,12 +1113,16 @@ class Scope {
* and also protects against a circular dependency from the operation to itself by temporarily
* setting the operation's result to a special expression.
*/
- private executeOp(opIndex: number): ts.Expression|null {
+ private executeOp(opIndex: number, skipOptional: boolean): ts.Expression|null {
const op = this.opQueue[opIndex];
if (!(op instanceof TcbOp)) {
return op;
}
+ if (skipOptional && op.optional) {
+ return null;
+ }
+
// Set the result of the operation in the queue to its circular fallback. If executing this
// operation results in a circular dependency, this will prevent an infinite loop and allow for
// the resolution of such cycles.
diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts
index b6c5322fbf..c15b1bc608 100644
--- a/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts
+++ b/packages/compiler-cli/src/ngtsc/typecheck/test/span_comments_spec.ts
@@ -158,7 +158,7 @@ describe('type check blocks diagnostics', () => {
}];
const TEMPLATE = ` {{ a || a }}`;
expect(tcbWithSpans(TEMPLATE, DIRECTIVES))
- .toContain('((_t2 /*23,24*/) || (_t2 /*28,29*/) /*23,29*/);');
+ .toContain('((_t1 /*23,24*/) || (_t1 /*28,29*/) /*23,29*/);');
});
});
});
diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts
index 8a11d6d87c..27cb1ac6e3 100644
--- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts
+++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts
@@ -55,7 +55,7 @@ describe('type check blocks', () => {
selector: '[dir]',
inputs: {inputA: 'inputA'},
}];
- expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t2: DirA = (null!); _t2.inputA = ("value");');
+ expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t1: DirA = (null!); _t1.inputA = ("value");');
});
it('should handle multiple bindings to the same property', () => {
@@ -67,8 +67,8 @@ describe('type check blocks', () => {
inputs: {inputA: 'inputA'},
}];
const block = tcb(TEMPLATE, DIRECTIVES);
- expect(block).toContain('_t2.inputA = (1);');
- expect(block).toContain('_t2.inputA = (2);');
+ expect(block).toContain('_t1.inputA = (1);');
+ expect(block).toContain('_t1.inputA = (2);');
});
it('should handle empty bindings', () => {
@@ -79,7 +79,7 @@ describe('type check blocks', () => {
selector: '[dir-a]',
inputs: {inputA: 'inputA'},
}];
- expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t2.inputA = (undefined);');
+ expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t1.inputA = (undefined);');
});
it('should handle bindings without value', () => {
@@ -90,7 +90,7 @@ describe('type check blocks', () => {
selector: '[dir-a]',
inputs: {inputA: 'inputA'},
}];
- expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t2.inputA = (undefined);');
+ expect(tcb(TEMPLATE, DIRECTIVES)).toContain('_t1.inputA = (undefined);');
});
it('should handle implicit vars on ng-template', () => {
@@ -124,7 +124,7 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2 = Dir.ngTypeCtor({ "fieldA": (((ctx).foo)), "fieldB": (null as any) });');
+ 'var _t1 = Dir.ngTypeCtor({ "fieldA": (((ctx).foo)), "fieldB": (null as any) });');
});
it('should handle multiple bindings to the same property', () => {
@@ -157,7 +157,7 @@ describe('type check blocks', () => {
}];
const block = tcb(TEMPLATE, DIRECTIVES);
expect(block).toContain(
- 'var _t2 = Dir.ngTypeCtor({ "color": (null as any), "strong": (null as any), "enabled": (null as any) });');
+ 'var _t1 = Dir.ngTypeCtor({ "color": (null as any), "strong": (null as any), "enabled": (null as any) });');
expect(block).toContain('"blue"; false; true;');
});
@@ -175,8 +175,8 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t3 = Dir.ngTypeCtor((null!)); ' +
- 'var _t2 = Dir.ngTypeCtor({ "input": (_t3) });');
+ 'var _t2 = Dir.ngTypeCtor((null!)); ' +
+ 'var _t1 = Dir.ngTypeCtor({ "input": (_t2) });');
});
it('should generate circular references between two directives correctly', () => {
@@ -204,9 +204,9 @@ describe('type check blocks', () => {
];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t4 = DirA.ngTypeCtor((null!)); ' +
- 'var _t3 = DirB.ngTypeCtor({ "inputB": (_t4) }); ' +
- 'var _t2 = DirA.ngTypeCtor({ "inputA": (_t3) });');
+ 'var _t3 = DirA.ngTypeCtor((null!)); ' +
+ 'var _t2 = DirB.ngTypeCtor({ "inputB": (_t3) }); ' +
+ 'var _t1 = DirA.ngTypeCtor({ "inputA": (_t2) });');
});
it('should handle empty bindings', () => {
@@ -247,12 +247,23 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2 = Dir.ngTypeCtor({ "fieldA": (((ctx).foo)) }); ' +
- 'var _t3: typeof Dir.ngAcceptInputType_fieldA = (null!); ' +
- '_t3 = (((ctx).foo));');
+ 'var _t1 = Dir.ngTypeCtor({ "fieldA": (((ctx).foo)) }); ' +
+ 'var _t2: typeof Dir.ngAcceptInputType_fieldA = (null!); ' +
+ '_t2 = (((ctx).foo));');
});
});
+ it('should only generate code for DOM elements that are actually referenced', () => {
+ const TEMPLATE = `
+
+
+ `;
+ const block = tcb(TEMPLATE);
+ expect(block).not.toContain('"div"');
+ expect(block).toContain('var _t1 = document.createElement("button");');
+ expect(block).toContain('(ctx).handle(_t1);');
+ });
+
it('should generate a forward element reference correctly', () => {
const TEMPLATE = `
{{ i.value }}
@@ -273,9 +284,7 @@ describe('type check blocks', () => {
selector: '[dir]',
exportAs: ['dir'],
}];
- expect(tcb(TEMPLATE, DIRECTIVES))
- .toContain(
- 'var _t1: Dir = (null!); "" + ((_t1).value); var _t2 = document.createElement("div");');
+ expect(tcb(TEMPLATE, DIRECTIVES)).toContain('var _t1: Dir = (null!); "" + ((_t1).value);');
});
it('should handle style and class bindings specially', () => {
@@ -301,7 +310,7 @@ describe('type check blocks', () => {
inputs: {'color': 'color', 'strong': 'strong', 'enabled': 'enabled'},
}];
const block = tcb(TEMPLATE, DIRECTIVES);
- expect(block).toContain('var _t2: Dir = (null!);');
+ expect(block).toContain('var _t1: Dir = (null!);');
expect(block).not.toContain('"color"');
expect(block).not.toContain('"strong"');
expect(block).not.toContain('"enabled"');
@@ -321,8 +330,8 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: Dir = (null!); ' +
- '_t2.input = (_t2);');
+ 'var _t1: Dir = (null!); ' +
+ '_t1.input = (_t1);');
});
it('should generate circular references between two directives correctly', () => {
@@ -348,11 +357,10 @@ describe('type check blocks', () => {
];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: DirA = (null!); ' +
- 'var _t3: DirB = (null!); ' +
- '_t2.inputA = (_t3); ' +
- 'var _t4 = document.createElement("div"); ' +
- '_t3.inputA = (_t2);');
+ 'var _t1: DirA = (null!); ' +
+ 'var _t2: DirB = (null!); ' +
+ '_t1.inputA = (_t2); ' +
+ '_t2.inputA = (_t1);');
});
it('should handle undeclared properties', () => {
@@ -368,7 +376,7 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: Dir = (null!); ' +
+ 'var _t1: Dir = (null!); ' +
'(((ctx).foo)); ');
});
@@ -385,9 +393,9 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: Dir = (null!); ' +
- 'var _t3: typeof _t2["fieldA"] = (null!); ' +
- '_t3 = (((ctx).foo)); ');
+ 'var _t1: Dir = (null!); ' +
+ 'var _t2: typeof _t1["fieldA"] = (null!); ' +
+ '_t2 = (((ctx).foo)); ');
});
it('should assign properties via element access for field names that are not JS identifiers',
@@ -404,8 +412,8 @@ describe('type check blocks', () => {
}];
const block = tcb(TEMPLATE, DIRECTIVES);
expect(block).toContain(
- 'var _t2: Dir = (null!); ' +
- '_t2["some-input.xs"] = (((ctx).foo)); ');
+ 'var _t1: Dir = (null!); ' +
+ '_t1["some-input.xs"] = (((ctx).foo)); ');
});
it('should handle a single property bound to multiple fields', () => {
@@ -421,8 +429,8 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: Dir = (null!); ' +
- '_t2.field2 = _t2.field1 = (((ctx).foo));');
+ 'var _t1: Dir = (null!); ' +
+ '_t1.field2 = _t1.field1 = (((ctx).foo));');
});
it('should handle a single property bound to multiple fields, where one of them is coerced',
@@ -440,9 +448,9 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: Dir = (null!); ' +
- 'var _t3: typeof Dir.ngAcceptInputType_field1 = (null!); ' +
- '_t2.field2 = _t3 = (((ctx).foo));');
+ 'var _t1: Dir = (null!); ' +
+ 'var _t2: typeof Dir.ngAcceptInputType_field1 = (null!); ' +
+ '_t1.field2 = _t2 = (((ctx).foo));');
});
it('should handle a single property bound to multiple fields, where one of them is undeclared',
@@ -460,8 +468,8 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: Dir = (null!); ' +
- '_t2.field2 = (((ctx).foo));');
+ 'var _t1: Dir = (null!); ' +
+ '_t1.field2 = (((ctx).foo));');
});
it('should use coercion types if declared', () => {
@@ -477,9 +485,9 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: Dir = (null!); ' +
- 'var _t3: typeof Dir.ngAcceptInputType_fieldA = (null!); ' +
- '_t3 = (((ctx).foo));');
+ 'var _t1: Dir = (null!); ' +
+ 'var _t2: typeof Dir.ngAcceptInputType_fieldA = (null!); ' +
+ '_t2 = (((ctx).foo));');
});
it('should use coercion types if declared, even when backing field is not declared', () => {
@@ -496,9 +504,9 @@ describe('type check blocks', () => {
}];
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
- 'var _t2: Dir = (null!); ' +
- 'var _t3: typeof Dir.ngAcceptInputType_fieldA = (null!); ' +
- '_t3 = (((ctx).foo));');
+ 'var _t1: Dir = (null!); ' +
+ 'var _t2: typeof Dir.ngAcceptInputType_fieldA = (null!); ' +
+ '_t2 = (((ctx).foo));');
});
it('should handle $any casts', () => {
@@ -561,7 +569,7 @@ describe('type check blocks', () => {
const TEMPLATE = `
`;
const block = tcb(TEMPLATE, DIRECTIVES);
expect(block).toContain(
- '_outputHelper(_t2["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });');
+ '_outputHelper(_t1["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });');
});
it('should emit a listener function with AnimationEvent for animation events', () => {
@@ -658,14 +666,14 @@ describe('type check blocks', () => {
it('should include null and undefined when enabled', () => {
const block = tcb(TEMPLATE, DIRECTIVES);
- expect(block).toContain('_t2.dirInput = (((ctx).a));');
+ expect(block).toContain('_t1.dirInput = (((ctx).a));');
expect(block).toContain('((ctx).b);');
});
it('should use the non-null assertion operator when disabled', () => {
const DISABLED_CONFIG:
TypeCheckingConfig = {...BASE_CONFIG, strictNullInputBindings: false};
const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG);
- expect(block).toContain('_t2.dirInput = (((ctx).a)!);');
+ expect(block).toContain('_t1.dirInput = (((ctx).a)!);');
expect(block).toContain('((ctx).b)!;');
});
});
@@ -674,7 +682,7 @@ describe('type check blocks', () => {
it('should check types of bindings when enabled', () => {
const TEMPLATE = `
`;
const block = tcb(TEMPLATE, DIRECTIVES);
- expect(block).toContain('_t2.dirInput = (((ctx).a));');
+ expect(block).toContain('_t1.dirInput = (((ctx).a));');
expect(block).toContain('((ctx).b);');
});
@@ -683,7 +691,7 @@ describe('type check blocks', () => {
const DISABLED_CONFIG:
TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfInputBindings: false};
const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG);
- expect(block).toContain('_t2.dirInput = ((((ctx).a) as any));');
+ expect(block).toContain('_t1.dirInput = ((((ctx).a) as any));');
expect(block).toContain('(((ctx).b) as any);');
});
@@ -692,7 +700,7 @@ describe('type check blocks', () => {
const DISABLED_CONFIG:
TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfInputBindings: false};
const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG);
- expect(block).toContain('_t2.dirInput = ((((((ctx).a)) === (((ctx).b))) as any));');
+ expect(block).toContain('_t1.dirInput = ((((((ctx).a)) === (((ctx).b))) as any));');
});
});
@@ -702,9 +710,9 @@ describe('type check blocks', () => {
it('should check types of directive outputs when enabled', () => {
const block = tcb(TEMPLATE, DIRECTIVES);
expect(block).toContain(
- '_outputHelper(_t2["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });');
+ '_outputHelper(_t1["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });');
expect(block).toContain(
- '_t1.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });');
+ '_t2.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });');
});
it('should not check types of directive outputs when disabled', () => {
const DISABLED_CONFIG:
@@ -713,7 +721,7 @@ describe('type check blocks', () => {
expect(block).toContain('function ($event: any): any { (ctx).foo($event); }');
// Note that DOM events are still checked, that is controlled by `checkTypeOfDomEvents`
expect(block).toContain(
- '_t1.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });');
+ '_t2.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });');
});
});
@@ -739,9 +747,9 @@ describe('type check blocks', () => {
it('should check types of DOM events when enabled', () => {
const block = tcb(TEMPLATE, DIRECTIVES);
expect(block).toContain(
- '_outputHelper(_t2["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });');
+ '_outputHelper(_t1["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });');
expect(block).toContain(
- '_t1.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });');
+ '_t2.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });');
});
it('should not check types of DOM events when disabled', () => {
const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfDomEvents: false};
@@ -749,7 +757,7 @@ describe('type check blocks', () => {
// Note that directive outputs are still checked, that is controlled by
// `checkTypeOfOutputEvents`
expect(block).toContain(
- '_outputHelper(_t2["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });');
+ '_outputHelper(_t1["outputField"]).subscribe(function ($event): any { (ctx).foo($event); });');
expect(block).toContain('function ($event: any): any { (ctx).foo($event); }');
});
});
@@ -785,7 +793,7 @@ describe('type check blocks', () => {
it('should trace references to a directive when enabled', () => {
const block = tcb(TEMPLATE, DIRECTIVES);
- expect(block).toContain('(_t2).value');
+ expect(block).toContain('(_t1).value');
});
it('should trace references to an when enabled', () => {
@@ -812,9 +820,9 @@ describe('type check blocks', () => {
it('should assign string value to the input when enabled', () => {
const block = tcb(TEMPLATE, DIRECTIVES);
- expect(block).toContain('_t2.disabled = ("");');
- expect(block).toContain('_t2.cols = ("3");');
- expect(block).toContain('_t2.rows = (2);');
+ expect(block).toContain('_t1.disabled = ("");');
+ expect(block).toContain('_t1.cols = ("3");');
+ expect(block).toContain('_t1.rows = (2);');
});
it('should use any for attributes but still check bound attributes when disabled', () => {
@@ -822,7 +830,7 @@ describe('type check blocks', () => {
const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG);
expect(block).not.toContain('"disabled"');
expect(block).not.toContain('"cols"');
- expect(block).toContain('_t2.rows = (2);');
+ expect(block).toContain('_t1.rows = (2);');
});
});
@@ -912,8 +920,8 @@ describe('type check blocks', () => {
TypeCheckingConfig = {...BASE_CONFIG, honorAccessModifiersForInputBindings: true};
const block = tcb(TEMPLATE, DIRECTIVES, enableChecks);
expect(block).toContain(
- 'var _t2: Dir = (null!); ' +
- '_t2["some-input.xs"] = (((ctx).foo)); ');
+ 'var _t1: Dir = (null!); ' +
+ '_t1["some-input.xs"] = (((ctx).foo)); ');
});
it('should assign restricted properties via property access', () => {
@@ -930,8 +938,8 @@ describe('type check blocks', () => {
TypeCheckingConfig = {...BASE_CONFIG, honorAccessModifiersForInputBindings: true};
const block = tcb(TEMPLATE, DIRECTIVES, enableChecks);
expect(block).toContain(
- 'var _t2: Dir = (null!); ' +
- '_t2.fieldA = (((ctx).foo)); ');
+ 'var _t1: Dir = (null!); ' +
+ '_t1.fieldA = (((ctx).foo)); ');
});
});
});
diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts
index 0a075efbf3..e2a14bce68 100644
--- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts
+++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker_spec.ts
@@ -66,7 +66,7 @@ runInEachFileSystem(os => {
const file1 = absoluteFrom('/file1.ts');
const file2 = absoluteFrom('/file2.ts');
const {program, templateTypeChecker, programStrategy} = setup([
- {fileName: file1, templates: {'Cmp1': '
'}},
+ {fileName: file1, templates: {'Cmp1': '{{value}}
'}},
{fileName: file2, templates: {'Cmp2': ' '}}
]);
@@ -74,7 +74,7 @@ runInEachFileSystem(os => {
const block = templateTypeChecker.getTypeCheckBlock(cmp1);
expect(block).not.toBeNull();
expect(block!.getText()).toMatch(/: i[0-9]\.Cmp1/);
- expect(block!.getText()).toContain(`document.createElement("div")`);
+ expect(block!.getText()).toContain(`value`);
});
it('should clear old inlines when necessary', () => {
@@ -223,43 +223,43 @@ runInEachFileSystem(os => {
const fileName = absoluteFrom('/main.ts');
const {program, templateTypeChecker} = setup([{
fileName,
- templates: {'Cmp': '
'},
+ templates: {'Cmp': '{{original}}
'},
}]);
const sf = getSourceFileOrError(program, fileName);
const cmp = getClass(sf, 'Cmp');
const tcbReal = templateTypeChecker.getTypeCheckBlock(cmp)!;
- expect(tcbReal.getText()).toContain('div');
+ expect(tcbReal.getText()).toContain('original');
- templateTypeChecker.overrideComponentTemplate(cmp, ' ');
+ templateTypeChecker.overrideComponentTemplate(cmp, '{{override}}
');
const tcbOverridden = templateTypeChecker.getTypeCheckBlock(cmp);
expect(tcbOverridden).not.toBeNull();
- expect(tcbOverridden!.getText()).not.toContain('div');
- expect(tcbOverridden!.getText()).toContain('span');
+ expect(tcbOverridden!.getText()).not.toContain('original');
+ expect(tcbOverridden!.getText()).toContain('override');
});
it('should clear overrides on request', () => {
const fileName = absoluteFrom('/main.ts');
const {program, templateTypeChecker} = setup([{
fileName,
- templates: {'Cmp': '
'},
+ templates: {'Cmp': '{{original}}
'},
}]);
const sf = getSourceFileOrError(program, fileName);
const cmp = getClass(sf, 'Cmp');
- templateTypeChecker.overrideComponentTemplate(cmp, ' ');
+ templateTypeChecker.overrideComponentTemplate(cmp, '{{override}}
');
const tcbOverridden = templateTypeChecker.getTypeCheckBlock(cmp)!;
- expect(tcbOverridden.getText()).not.toContain('div');
- expect(tcbOverridden.getText()).toContain('span');
+ expect(tcbOverridden.getText()).not.toContain('original');
+ expect(tcbOverridden.getText()).toContain('override');
templateTypeChecker.resetOverrides();
- // The template should be back to the original, which has and not .
+ // The template should be back to the original.
const tcbReal = templateTypeChecker.getTypeCheckBlock(cmp)!;
- expect(tcbReal.getText()).toContain('div');
- expect(tcbReal.getText()).not.toContain('span');
+ expect(tcbReal.getText()).toContain('original');
+ expect(tcbReal.getText()).not.toContain('override');
});
it('should override a template and make use of previously unused directives', () => {