diff --git a/packages/compiler-cli/src/ngtsc/typecheck/README.md b/packages/compiler-cli/src/ngtsc/typecheck/README.md
index 1e32d15e2eb..d84bb4e23b0 100644
--- a/packages/compiler-cli/src/ngtsc/typecheck/README.md
+++ b/packages/compiler-cli/src/ngtsc/typecheck/README.md
@@ -24,7 +24,7 @@ TCBs are not ever emitted, nor are they referenced from any other code (they're
Given a component `SomeCmp`, its TCB takes the form of a function:
```typescript
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
// TCB code
}
```
@@ -38,15 +38,15 @@ The component context is the theoretical component instance associated with the
For example, if `SomeCmp`'s template has an interpolation expression `{{foo.bar}}`, this suggests that `SomeCmp` has a property `foo`, and that `foo` itself is an object with a property `bar` (or in a type sense, that the type of `SomeCmp.foo` has a property `bar`).
-Such a binding is expressed in the TCB function, using the `ctx` parameter as the component instance:
+Such a binding is expressed in the TCB function, using the `this` parameter as the component instance:
```typescript
-function tcb(ctx: SomeCmp): void {
- '' + ctx.foo.bar;
+function tcb(this: SomeCmp): void {
+ '' + this.foo.bar;
}
```
-If `SomeCmp` does not have a `foo` property, then TypeScript will produce a type error/diagnostic for the expression `ctx.foo`. If `ctx.foo` does exist, but is not of a type that has a `bar` property, then TypeScript will catch that too. By mapping the template expression `{{foo.bar}}` to TypeScript code, the compiler has captured its _intent_ in the TCB in a way that TypeScript can validate.
+If `SomeCmp` does not have a `foo` property, then TypeScript will produce a type error/diagnostic for the expression `this.foo`. If `this.foo` does exist, but is not of a type that has a `bar` property, then TypeScript will catch that too. By mapping the template expression `{{foo.bar}}` to TypeScript code, the compiler has captured its _intent_ in the TCB in a way that TypeScript can validate.
#### Types of template declarations
@@ -62,7 +62,7 @@ declares a single `` element with a local ref `#name`, meaning that withi
Within the TCB, the `` element is treated as a declaration, and the compiler leverages the powerful type inference of `document.createElement`:
```typescript
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
var _t1 = document.createElement('input');
'' + _t1.value;
}
@@ -83,13 +83,13 @@ Just like with HTML elements, directives present on elements within the template
The TCB for this template looks like:
```typescript
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
var _t1: OtherCmp = null!;
- _t1.foo = ctx.bar;
+ _t1.foo = this.bar;
}
```
-Since `` is a component, the TCB declares `_t1` to be of that component's type. This allows for the binding `[foo]="bar"` to be expressed in TypeScript as `_t1.foo = ctx.bar` - an assignment to `OtherCmp`'s `@Input` for `foo` of the `bar` property from the template's context. TypeScript can then type check this operation and produce diagnostics if the type of `ctx.bar` is not assignable to the `_t1.foo` property which backs the `@Input`.
+Since `` is a component, the TCB declares `_t1` to be of that component's type. This allows for the binding `[foo]="bar"` to be expressed in TypeScript as `_t1.foo = this.bar` - an assignment to `OtherCmp`'s `@Input` for `foo` of the `bar` property from the template's context. TypeScript can then type check this operation and produce diagnostics if the type of `this.bar` is not assignable to the `_t1.foo` property which backs the `@Input`.
#### Generic directives & type constructors
@@ -147,9 +147,9 @@ This type constructor can then be used to infer the instance type of a usage of
Would use the above type constructor in its TCB:
```typescript
-function tcb(ctx: SomeCmp): void {
- var _t1 = ctor1({ngForOf: ctx.users});
- // Assuming ctx.users is User[], then _t1 is inferred as NgFor.
+function tcb(this: SomeCmp): void {
+ var _t1 = ctor1({ngForOf: this.users});
+ // Assuming this.users is User[], then _t1 is inferred as NgFor.
}
```
@@ -180,9 +180,9 @@ In the TCB, the template context of this nested template is itself a declaration
```typescript
declare function ctor1(inputs: {ngForOf?: Iterable}): NgFor;
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
// _t1 is the NgFor directive instance, inferred as NgFor.
- var _t1 = ctor1({ngForOf: ctx.users});
+ var _t1 = ctor1({ngForOf: this.users});
// _t2 is the context type for the embedded views created by the NgFor structural directive.
var _t2: any;
@@ -221,9 +221,9 @@ The `typecheck` system is aware of the presence of this method on any structural
```typescript
declare function ctor1(inputs: {ngForOf?: Iterable}): NgFor;
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
// _t1 is the NgFor directive instance, inferred as NgFor.
- var _t1 = ctor1({ngForOf: ctx.users});
+ var _t1 = ctor1({ngForOf: this.users});
// _t2 is the context type for the embedded views created by the NgFor structural directive.
var _t2: any;
@@ -258,15 +258,15 @@ Because the `NgFor` directive _declared_ to the template type checking engine wh
Obviously, if `user` is potentially `null`, then this `NgIf` is intended to only show the `
` when `user` actually has a value. However, from a type-checking perspective, the expression `user.name` is not legal if `user` is potentially `null`. So if this template was rendered into a TCB as:
```typescript
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
// Type of the NgIf directive instance.
var _t1: NgIf;
// Binding *ngIf="user != null".
- _t1.ngIf = ctx.user !== null;
+ _t1.ngIf = this.user !== null;
// Nested template interpolation `{{user.name}}`
- '' + ctx.user.name;
+ '' + this.user.name;
}
```
@@ -286,23 +286,23 @@ export class NgIf {
The presence and type of this static property tells the template type-checking engine to reflect the bound expression for its `ngIf` input as a guard for any embedded views created by the structural directive. This produces a TCB:
```typescript
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
// Type of the NgIf directive instance.
var _t1: NgIf;
// Binding *ngIf="user != null".
- _t1.ngIf = ctx.user !== null;
+ _t1.ngIf = this.user !== null;
// Guard generated due to the `ngTemplateGuard_ngIf` declaration by the NgIf directive.
if (user !== null) {
// Nested template interpolation `{{user.name}}`.
- // `ctx.user` here is appropriately narrowed to be non-nullable.
- '' + ctx.user.name;
+ // `this.user` here is appropriately narrowed to be non-nullable.
+ '' + this.user.name;
}
}
```
-The guard expression causes TypeScript to narrow the type of `ctx.user` within the `if` block and identify that `ctx.user` cannot be `null` within the embedded view context, just as `NgIf` itself does during rendering.
+The guard expression causes TypeScript to narrow the type of `this.user` within the `if` block and identify that `this.user` cannot be `null` within the embedded view context, just as `NgIf` itself does during rendering.
### Generation process
@@ -393,7 +393,7 @@ Here, type-checking the `[in]` binding requires knowing the type of `ref`, which
```typescript
declare function ctor1(inputs: {in?: T}): GenericCmp;
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
// Not legal: cannot refer to t1 (ref) before its declaration.
var t1 = ctor1({in: t1.value});
@@ -408,7 +408,7 @@ To get around this, `TcbOp`s may optionally provide a fallback value via a `circ
```typescript
declare function ctor1(inputs: {in?: T}): GenericCmp;
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
// Generated to break the cycle for `ref` - infers a placeholder
// type for the component without using any of its input bindings.
var t1 = ctor1(null!);
@@ -445,16 +445,16 @@ Consider a template expression of the form:
The generated TCB code for this expression would look like:
```typescript
-'' + ctx.foo.bar;
+'' + this.foo.bar;
```
What actually gets generated for this expression looks more like:
```typescript
-'' + (ctx.foo /* 3,5 */).bar /* 3,9 */;
+'' + (this.foo /* 3,5 */).bar /* 3,9 */;
```
-The trailing comment for each node in the TCB indicates the template offsets for the corresponding template nodes. If for example TypeScript returns a diagnostic for the `ctx.foo` part of the expression (such as if `foo` is not a valid property on the component context), the attached comment can be used to map this diagnostic back to the original template's `foo` node.
+The trailing comment for each node in the TCB indicates the template offsets for the corresponding template nodes. If for example TypeScript returns a diagnostic for the `this.foo` part of the expression (such as if `foo` is not a valid property on the component context), the attached comment can be used to map this diagnostic back to the original template's `foo` node.
#### `TemplateId`
@@ -548,7 +548,7 @@ Additions of such inline type checking code have significant ramifications on th
A similar problem exists for generic components and the declaration of TCBs. A TCB function must also copy the generic bounds of its context component:
```typescript
-function tcb(ctx: SomeCmp): void {
+function tcb(this: SomeCmp): void {
/* tcb code */
}
```
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 859a51c40c0..17b140ae2fe 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
@@ -117,7 +117,7 @@ export function generateTypeCheckBlock(
}
}
- const paramList = [tcbCtxParam(ref.node, ctxRawType.typeName, typeArguments)];
+ const paramList = [tcbThisParam(ref.node, ctxRawType.typeName, typeArguments)];
const scopeStatements = scope.render();
const innerBody = ts.factory.createBlock([
@@ -1099,7 +1099,7 @@ class TcbUnclaimedOutputsOp extends TcbOp {
/**
* A `TcbOp` which generates a completion point for the component context.
*
- * This completion point looks like `ctx. ;` in the TCB output, and does not produce diagnostics.
+ * This completion point looks like `this. ;` in the TCB output, and does not produce diagnostics.
* TypeScript autocompletion APIs can be used at this completion point (after the '.') to produce
* autocompletion results of properties and methods from the template's component context.
*/
@@ -1111,7 +1111,7 @@ class TcbComponentContextCompletionOp extends TcbOp {
override readonly optional = false;
override execute(): null {
- const ctx = ts.factory.createIdentifier('ctx');
+ const ctx = ts.factory.createThis();
const ctxDot = ts.factory.createPropertyAccessExpression(ctx, '');
markIgnoreDiagnostics(ctxDot);
addExpressionIdentifier(ctxDot, ExpressionIdentifier.COMPONENT_COMPLETION);
@@ -1636,9 +1636,10 @@ interface TcbBoundInput {
}
/**
- * Create the `ctx` parameter to the top-level TCB function, with the given generic type arguments.
+ * Create the `this` parameter to the top-level TCB function, with the given generic type
+ * arguments.
*/
-function tcbCtxParam(
+function tcbThisParam(
node: ClassDeclaration, name: ts.EntityName,
typeArguments: ts.TypeNode[]|undefined): ts.ParameterDeclaration {
const type = ts.factory.createTypeReferenceNode(name, typeArguments);
@@ -1646,7 +1647,7 @@ function tcbCtxParam(
/* decorators */ undefined,
/* modifiers */ undefined,
/* dotDotDotToken */ undefined,
- /* name */ 'ctx',
+ /* name */ 'this',
/* questionToken */ undefined,
/* type */ type,
/* initializer */ undefined);
@@ -1708,7 +1709,7 @@ class TcbExpressionTranslator {
// Therefore if `resolve` is called on an `ImplicitReceiver`, it's because no outer
// PropertyRead/Call resolved to a variable or reference, and therefore this is a
// property read or method call on the component context itself.
- return ts.factory.createIdentifier('ctx');
+ return ts.factory.createThis();
} else if (ast instanceof BindingPipe) {
const expr = this.translate(ast.exp);
const pipeRef = this.tcb.getPipeByName(ast.name);
@@ -1985,13 +1986,13 @@ function tcbCreateEventHandler(
/* type */ eventParamType);
addExpressionIdentifier(eventParam, ExpressionIdentifier.EVENT_PARAMETER);
- return ts.factory.createFunctionExpression(
- /* modifier */ undefined,
- /* asteriskToken */ undefined,
- /* name */ undefined,
+ // Return an arrow function instead of a function expression to preserve the `this` context.
+ return ts.factory.createArrowFunction(
+ /* modifiers */ undefined,
/* typeParameters */ undefined,
/* parameters */[eventParam],
/* type */ ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword),
+ /* equalsGreaterThanToken */ undefined,
/* body */ ts.factory.createBlock([body]));
}
diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts
index 73d5842f2fa..00615193878 100644
--- a/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts
+++ b/packages/compiler-cli/src/ngtsc/typecheck/test/diagnostics_spec.ts
@@ -585,6 +585,29 @@ class TestComponent {
expect(messages).toEqual(
[`TestComponent.html(3, 30): Type 'HTMLElement' is not assignable to type 'string'.`]);
});
+
+ it('allows access to protected members', () => {
+ const messages = diagnose(``, `
+ class TestComponent {
+ protected message = 'Hello world';
+ protected doFoo(): void {}
+ }`);
+
+ expect(messages).toEqual([]);
+ });
+
+ it('disallows access to private members', () => {
+ const messages = diagnose(``, `
+ class TestComponent {
+ private message = 'Hello world';
+ private doFoo(): void {}
+ }`);
+
+ expect(messages).toEqual([
+ `TestComponent.html(1, 18): Property 'doFoo' is private and only accessible within class 'TestComponent'.`,
+ `TestComponent.html(1, 30): Property 'message' is private and only accessible within class 'TestComponent'.`
+ ]);
+ });
});
describe('method call spans', () => {
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 79508b5a302..22a914f14f3 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
@@ -14,23 +14,24 @@ describe('type check blocks diagnostics', () => {
describe('parse spans', () => {
it('should annotate unary ops', () => {
- expect(tcbWithSpans('{{ -a }}')).toContain('(-((ctx).a /*4,5*/) /*4,5*/) /*3,5*/');
+ expect(tcbWithSpans('{{ -a }}')).toContain('(-((this).a /*4,5*/) /*4,5*/) /*3,5*/');
});
it('should annotate binary ops', () => {
expect(tcbWithSpans('{{ a + b }}'))
- .toContain('(((ctx).a /*3,4*/) /*3,4*/) + (((ctx).b /*7,8*/) /*7,8*/) /*3,8*/');
+ .toContain('(((this).a /*3,4*/) /*3,4*/) + (((this).b /*7,8*/) /*7,8*/) /*3,8*/');
});
it('should annotate conditions', () => {
expect(tcbWithSpans('{{ a ? b : c }}'))
.toContain(
- '(((ctx).a /*3,4*/) /*3,4*/ ? ((ctx).b /*7,8*/) /*7,8*/ : (((ctx).c /*11,12*/) /*11,12*/)) /*3,12*/');
+ '(((this).a /*3,4*/) /*3,4*/ ? ((this).b /*7,8*/) /*7,8*/ : (((this).c /*11,12*/) /*11,12*/)) /*3,12*/');
});
it('should annotate interpolations', () => {
expect(tcbWithSpans('{{ hello }} {{ world }}'))
- .toContain('"" + (((ctx).hello /*3,8*/) /*3,8*/) + (((ctx).world /*15,20*/) /*15,20*/)');
+ .toContain(
+ '"" + (((this).hello /*3,8*/) /*3,8*/) + (((this).world /*15,20*/) /*15,20*/)');
});
it('should annotate literal map expressions', () => {
@@ -39,7 +40,7 @@ describe('type check blocks diagnostics', () => {
const TEMPLATE = '{{ m({foo: a, bar: b}) }}';
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '(ctx).m /*3,4*/({ "foo": ((ctx).a /*11,12*/) /*11,12*/, "bar": ((ctx).b /*19,20*/) /*19,20*/ } /*5,21*/) /*3,22*/');
+ '(this).m /*3,4*/({ "foo": ((this).a /*11,12*/) /*11,12*/, "bar": ((this).b /*19,20*/) /*19,20*/ } /*5,21*/) /*3,22*/');
});
it('should annotate literal map expressions with shorthand declarations', () => {
@@ -48,13 +49,13 @@ describe('type check blocks diagnostics', () => {
const TEMPLATE = '{{ m({a, b}) }}';
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '((ctx).m /*3,4*/({ "a": ((ctx).a /*6,7*/) /*6,7*/, "b": ((ctx).b /*9,10*/) /*9,10*/ } /*5,11*/) /*3,12*/)');
+ '((this).m /*3,4*/({ "a": ((this).a /*6,7*/) /*6,7*/, "b": ((this).b /*9,10*/) /*9,10*/ } /*5,11*/) /*3,12*/)');
});
it('should annotate literal array expressions', () => {
const TEMPLATE = '{{ [a, b] }}';
expect(tcbWithSpans(TEMPLATE))
- .toContain('[((ctx).a /*4,5*/) /*4,5*/, ((ctx).b /*7,8*/) /*7,8*/] /*3,9*/');
+ .toContain('[((this).a /*4,5*/) /*4,5*/, ((this).b /*7,8*/) /*7,8*/] /*3,9*/');
});
it('should annotate literals', () => {
@@ -64,104 +65,104 @@ describe('type check blocks diagnostics', () => {
it('should annotate non-null assertions', () => {
const TEMPLATE = `{{ a! }}`;
- expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*3,4*/) /*3,4*/)! /*3,5*/');
+ expect(tcbWithSpans(TEMPLATE)).toContain('(((this).a /*3,4*/) /*3,4*/)! /*3,5*/');
});
it('should annotate prefix not', () => {
const TEMPLATE = `{{ !a }}`;
- expect(tcbWithSpans(TEMPLATE)).toContain('!(((ctx).a /*4,5*/) /*4,5*/) /*3,5*/');
+ expect(tcbWithSpans(TEMPLATE)).toContain('!(((this).a /*4,5*/) /*4,5*/) /*3,5*/');
});
it('should annotate method calls', () => {
const TEMPLATE = `{{ method(a, b) }}`;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '(ctx).method /*3,9*/(((ctx).a /*10,11*/) /*10,11*/, ((ctx).b /*13,14*/) /*13,14*/) /*3,15*/');
+ '(this).method /*3,9*/(((this).a /*10,11*/) /*10,11*/, ((this).b /*13,14*/) /*13,14*/) /*3,15*/');
});
it('should annotate safe calls', () => {
const TEMPLATE = `{{ method?.(a, b) }}`;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '((null as any ? (((ctx).method /*3,9*/) /*3,9*/)!(((ctx).a /*12,13*/) /*12,13*/, ((ctx).b /*15,16*/) /*15,16*/) : undefined) /*3,17*/)');
+ '((null as any ? (((this).method /*3,9*/) /*3,9*/)!(((this).a /*12,13*/) /*12,13*/, ((this).b /*15,16*/) /*15,16*/) : undefined) /*3,17*/)');
});
it('should annotate method calls of variables', () => {
const TEMPLATE = `{{ method(a, b) }}`;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '_t2 /*27,33*/(((ctx).a /*34,35*/) /*34,35*/, ((ctx).b /*37,38*/) /*37,38*/) /*27,39*/');
+ '_t2 /*27,33*/(((this).a /*34,35*/) /*34,35*/, ((this).b /*37,38*/) /*37,38*/) /*27,39*/');
});
it('should annotate function calls', () => {
const TEMPLATE = `{{ method(a)(b, c) }}`;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '(ctx).method /*3,9*/(((ctx).a /*10,11*/) /*10,11*/) /*3,12*/(((ctx).b /*13,14*/) /*13,14*/, ((ctx).c /*16,17*/) /*16,17*/) /*3,18*/');
+ '(this).method /*3,9*/(((this).a /*10,11*/) /*10,11*/) /*3,12*/(((this).b /*13,14*/) /*13,14*/, ((this).c /*16,17*/) /*16,17*/) /*3,18*/');
});
it('should annotate property access', () => {
const TEMPLATE = `{{ a.b.c }}`;
expect(tcbWithSpans(TEMPLATE))
- .toContain('((((((ctx).a /*3,4*/) /*3,4*/).b /*5,6*/) /*3,6*/).c /*7,8*/) /*3,8*/');
+ .toContain('((((((this).a /*3,4*/) /*3,4*/).b /*5,6*/) /*3,6*/).c /*7,8*/) /*3,8*/');
});
it('should annotate property writes', () => {
const TEMPLATE = ``;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '(((((((ctx).a /*14,15*/) /*14,15*/).b /*16,17*/) /*14,17*/).c /*18,19*/) /*14,23*/ = (((ctx).d /*22,23*/) /*22,23*/)) /*14,23*/');
+ '(((((((this).a /*14,15*/) /*14,15*/).b /*16,17*/) /*14,17*/).c /*18,19*/) /*14,23*/ = (((this).d /*22,23*/) /*22,23*/)) /*14,23*/');
});
it('should $event property writes', () => {
const TEMPLATE = ``;
expect(tcbWithSpans(TEMPLATE))
- .toContain('(((ctx).a /*14,15*/) /*14,24*/ = ($event /*18,24*/)) /*14,24*/;');
+ .toContain('(((this).a /*14,15*/) /*14,24*/ = ($event /*18,24*/)) /*14,24*/;');
});
it('should annotate keyed property access', () => {
const TEMPLATE = `{{ a[b] }}`;
expect(tcbWithSpans(TEMPLATE))
- .toContain('(((ctx).a /*3,4*/) /*3,4*/)[((ctx).b /*5,6*/) /*5,6*/] /*3,7*/');
+ .toContain('(((this).a /*3,4*/) /*3,4*/)[((this).b /*5,6*/) /*5,6*/] /*3,7*/');
});
it('should annotate keyed property writes', () => {
const TEMPLATE = ``;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '((((ctx).a /*14,15*/) /*14,15*/)[((ctx).b /*16,17*/) /*16,17*/] = (((ctx).c /*21,22*/) /*21,22*/)) /*14,22*/');
+ '((((this).a /*14,15*/) /*14,15*/)[((this).b /*16,17*/) /*16,17*/] = (((this).c /*21,22*/) /*21,22*/)) /*14,22*/');
});
it('should annotate safe property access', () => {
const TEMPLATE = `{{ a?.b }}`;
expect(tcbWithSpans(TEMPLATE))
- .toContain('(null as any ? (((ctx).a /*3,4*/) /*3,4*/)!.b /*6,7*/ : undefined) /*3,7*/');
+ .toContain('(null as any ? (((this).a /*3,4*/) /*3,4*/)!.b /*6,7*/ : undefined) /*3,7*/');
});
it('should annotate safe method calls', () => {
const TEMPLATE = `{{ a?.method(b) }}`;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '((null as any ? (null as any ? (((ctx).a /*3,4*/) /*3,4*/)!.method /*6,12*/ : undefined) /*3,12*/!(((ctx).b /*13,14*/) /*13,14*/) : undefined) /*3,15*/)');
+ '((null as any ? (null as any ? (((this).a /*3,4*/) /*3,4*/)!.method /*6,12*/ : undefined) /*3,12*/!(((this).b /*13,14*/) /*13,14*/) : undefined) /*3,15*/)');
});
it('should annotate safe keyed reads', () => {
const TEMPLATE = `{{ a?.[0] }}`;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '(null as any ? (((ctx).a /*3,4*/) /*3,4*/)![0 /*7,8*/] /*3,9*/ : undefined) /*3,9*/');
+ '(null as any ? (((this).a /*3,4*/) /*3,4*/)![0 /*7,8*/] /*3,9*/ : undefined) /*3,9*/');
});
it('should annotate $any casts', () => {
const TEMPLATE = `{{ $any(a) }}`;
- expect(tcbWithSpans(TEMPLATE)).toContain('(((ctx).a /*8,9*/) /*8,9*/ as any) /*3,10*/');
+ expect(tcbWithSpans(TEMPLATE)).toContain('(((this).a /*8,9*/) /*8,9*/ as any) /*3,10*/');
});
it('should annotate chained expressions', () => {
const TEMPLATE = ``;
expect(tcbWithSpans(TEMPLATE))
.toContain(
- '(((ctx).a /*14,15*/) /*14,15*/, ((ctx).b /*17,18*/) /*17,18*/, ((ctx).c /*20,21*/) /*20,21*/) /*14,21*/');
+ '(((this).a /*14,15*/) /*14,15*/, ((this).b /*17,18*/) /*17,18*/, ((this).c /*20,21*/) /*20,21*/) /*14,21*/');
});
it('should annotate pipe usages', () => {
@@ -174,7 +175,7 @@ describe('type check blocks diagnostics', () => {
const block = tcbWithSpans(TEMPLATE, PIPES);
expect(block).toContain('var _pipe1: i0.TestPipe = null!');
expect(block).toContain(
- '(_pipe1.transform /*7,11*/(((ctx).a /*3,4*/) /*3,4*/, ((ctx).b /*12,13*/) /*12,13*/) /*3,13*/);');
+ '(_pipe1.transform /*7,11*/(((this).a /*3,4*/) /*3,4*/, ((this).b /*12,13*/) /*12,13*/) /*3,13*/);');
});
describe('attaching multiple comments for multiple references', () => {
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 7747242f115..d3a3ad23791 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
@@ -15,22 +15,22 @@ describe('type check blocks', () => {
beforeEach(() => initMockFileSystem('Native'));
it('should generate a basic block for a binding', () => {
- expect(tcb('{{hello}} {{world}}')).toContain('"" + (((ctx).hello)) + (((ctx).world));');
+ expect(tcb('{{hello}} {{world}}')).toContain('"" + (((this).hello)) + (((this).world));');
});
it('should generate literal map expressions', () => {
const TEMPLATE = '{{ method({foo: a, bar: b}) }}';
- expect(tcb(TEMPLATE)).toContain('(ctx).method({ "foo": ((ctx).a), "bar": ((ctx).b) })');
+ expect(tcb(TEMPLATE)).toContain('(this).method({ "foo": ((this).a), "bar": ((this).b) })');
});
it('should generate literal array expressions', () => {
const TEMPLATE = '{{ method([a, b]) }}';
- expect(tcb(TEMPLATE)).toContain('(ctx).method([((ctx).a), ((ctx).b)])');
+ expect(tcb(TEMPLATE)).toContain('(this).method([((this).a), ((this).b)])');
});
it('should handle non-null assertions', () => {
const TEMPLATE = `{{a!}}`;
- expect(tcb(TEMPLATE)).toContain('((((ctx).a))!)');
+ expect(tcb(TEMPLATE)).toContain('((((this).a))!)');
});
it('should handle unary - operator', () => {
@@ -40,20 +40,20 @@ describe('type check blocks', () => {
it('should handle keyed property access', () => {
const TEMPLATE = `{{a[b]}}`;
- expect(tcb(TEMPLATE)).toContain('(((ctx).a))[((ctx).b)]');
+ expect(tcb(TEMPLATE)).toContain('(((this).a))[((this).b)]');
});
it('should handle nested ternary expressions', () => {
const TEMPLATE = `{{a ? b : c ? d : e}}`;
expect(tcb(TEMPLATE))
- .toContain('(((ctx).a) ? ((ctx).b) : ((((ctx).c) ? ((ctx).d) : (((ctx).e)))))');
+ .toContain('(((this).a) ? ((this).b) : ((((this).c) ? ((this).d) : (((this).e)))))');
});
it('should handle nullish coalescing operator', () => {
- expect(tcb('{{ a ?? b }}')).toContain('((((ctx).a)) ?? (((ctx).b)))');
- expect(tcb('{{ a ?? b ?? c }}')).toContain('(((((ctx).a)) ?? (((ctx).b))) ?? (((ctx).c)))');
+ expect(tcb('{{ a ?? b }}')).toContain('((((this).a)) ?? (((this).b)))');
+ expect(tcb('{{ a ?? b ?? c }}')).toContain('(((((this).a)) ?? (((this).b))) ?? (((this).c)))');
expect(tcb('{{ (a ?? b) + (c ?? e) }}'))
- .toContain('(((((ctx).a)) ?? (((ctx).b))) + ((((ctx).c)) ?? (((ctx).e))))');
+ .toContain('(((((this).a)) ?? (((this).b))) + ((((this).c)) ?? (((this).e))))');
});
it('should handle attribute values for directive inputs', () => {
@@ -141,7 +141,7 @@ describe('type check blocks', () => {
expect(actual).toContain(
'const _ctor1: (init: Pick, "fieldA" | "fieldB">) => i0.Dir = null!;');
expect(actual).toContain(
- 'var _t1 = _ctor1({ "fieldA": (((ctx).foo)), "fieldB": null as any });');
+ 'var _t1 = _ctor1({ "fieldA": (((this).foo)), "fieldB": null as any });');
});
it('should handle multiple bindings to the same property', () => {
@@ -273,7 +273,7 @@ describe('type check blocks', () => {
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' +
- '_t1 = (((ctx).foo));');
+ '_t1 = (((this).foo));');
});
});
@@ -333,7 +333,7 @@ describe('type check blocks', () => {
];
const block = tcb(TEMPLATE, DIRECTIVES);
expect(block).toContain('var _t1: i0.HasInput = null!');
- expect(block).toContain('_t1.input = (((ctx).value));');
+ expect(block).toContain('_t1.input = (((this).value));');
expect(block).toContain('var _t2: i0.HasOutput = null!');
expect(block).toContain('_t2["output"]');
expect(block).toContain('var _t4: i0.HasReference = null!');
@@ -376,7 +376,7 @@ describe('type check blocks', () => {
`;
const block = tcb(TEMPLATE);
- expect(block).toContain('((ctx).a); ((ctx).b);');
+ expect(block).toContain('((this).a); ((this).b);');
// There should be no assignments to the class or style properties.
expect(block).not.toContain('.class = ');
@@ -463,7 +463,7 @@ describe('type check blocks', () => {
}];
const block = tcb(TEMPLATE, DIRECTIVES);
expect(block).not.toContain('var _t1: Dir = null!;');
- expect(block).toContain('(((ctx).foo)); ');
+ expect(block).toContain('(((this).foo)); ');
});
it('should assign restricted properties to temp variables by default', () => {
@@ -481,7 +481,7 @@ describe('type check blocks', () => {
.toContain(
'var _t1: i0.Dir = null!; ' +
'var _t2: (typeof _t1)["fieldA"] = null!; ' +
- '_t2 = (((ctx).foo)); ');
+ '_t2 = (((this).foo)); ');
});
it('should assign properties via element access for field names that are not JS identifiers',
@@ -499,7 +499,7 @@ describe('type check blocks', () => {
const block = tcb(TEMPLATE, DIRECTIVES);
expect(block).toContain(
'var _t1: i0.Dir = null!; ' +
- '_t1["some-input.xs"] = (((ctx).foo)); ');
+ '_t1["some-input.xs"] = (((this).foo)); ');
});
it('should handle a single property bound to multiple fields', () => {
@@ -516,7 +516,7 @@ describe('type check blocks', () => {
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
'var _t1: i0.Dir = null!; ' +
- '_t1.field2 = _t1.field1 = (((ctx).foo));');
+ '_t1.field2 = _t1.field1 = (((this).foo));');
});
it('should handle a single property bound to multiple fields, where one of them is coerced',
@@ -536,7 +536,7 @@ describe('type check blocks', () => {
.toContain(
'var _t1: typeof i0.Dir.ngAcceptInputType_field1 = null!; ' +
'var _t2: i0.Dir = null!; ' +
- '_t2.field2 = _t1 = (((ctx).foo));');
+ '_t2.field2 = _t1 = (((this).foo));');
});
it('should handle a single property bound to multiple fields, where one of them is undeclared',
@@ -555,7 +555,7 @@ describe('type check blocks', () => {
expect(tcb(TEMPLATE, DIRECTIVES))
.toContain(
'var _t1: i0.Dir = null!; ' +
- '_t1.field2 = (((ctx).foo));');
+ '_t1.field2 = (((this).foo));');
});
it('should use coercion types if declared', () => {
@@ -573,7 +573,7 @@ describe('type check blocks', () => {
expect(block).not.toContain('var _t1: Dir = null!;');
expect(block).toContain(
'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' +
- '_t1 = (((ctx).foo));');
+ '_t1 = (((this).foo));');
});
it('should use coercion types if declared, even when backing field is not declared', () => {
@@ -592,25 +592,25 @@ describe('type check blocks', () => {
expect(block).not.toContain('var _t1: Dir = null!;');
expect(block).toContain(
'var _t1: typeof i0.Dir.ngAcceptInputType_fieldA = null!; ' +
- '_t1 = (((ctx).foo));');
+ '_t1 = (((this).foo));');
});
it('should handle $any casts', () => {
const TEMPLATE = `{{$any(a)}}`;
const block = tcb(TEMPLATE);
- expect(block).toContain('(((ctx).a) as any)');
+ expect(block).toContain('(((this).a) as any)');
});
it('should handle $any accessed through `this`', () => {
const TEMPLATE = `{{this.$any(a)}}`;
const block = tcb(TEMPLATE);
- expect(block).toContain('((ctx).$any(((ctx).a)))');
+ expect(block).toContain('((this).$any(((this).a)))');
});
it('should handle $any accessed through a property read', () => {
const TEMPLATE = `{{foo.$any(a)}}`;
const block = tcb(TEMPLATE);
- expect(block).toContain('((((ctx).foo)).$any(((ctx).a)))');
+ expect(block).toContain('((((this).foo)).$any(((this).a)))');
});
describe('experimental DOM checking via lib.dom.d.ts', () => {
@@ -636,7 +636,7 @@ describe('type check blocks', () => {
}];
const TEMPLATE = `