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 = `
{{person.name}}
`; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('if (i0.NgIf.ngTemplateGuard_ngIf(_t1, ((ctx).person)))'); + expect(block).toContain('if (i0.NgIf.ngTemplateGuard_ngIf(_t1, ((this).person)))'); }); it('should emit binding guards', () => { @@ -652,7 +652,7 @@ describe('type check blocks', () => { }]; const TEMPLATE = `
{{person.name}}
`; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('if ((((ctx).person)) !== (null))'); + expect(block).toContain('if ((((this).person)) !== (null))'); }); it('should not emit guards when the child scope is empty', () => { @@ -683,34 +683,33 @@ describe('type check blocks', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_t1["outputField"].subscribe(function ($event): any { (ctx).foo($event); });'); + '_t1["outputField"].subscribe(($event): any => { (this).foo($event); });'); }); it('should emit a listener function with AnimationEvent for animation events', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE); - expect(block).toContain('function ($event: i1.AnimationEvent): any { (ctx).foo($event); }'); + expect(block).toContain('($event: i1.AnimationEvent): any => { (this).foo($event); }'); }); it('should emit addEventListener calls for unclaimed outputs', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE); expect(block).toContain( - '_t1.addEventListener("event", function ($event): any { (ctx).foo($event); });'); + '_t1.addEventListener("event", ($event): any => { (this).foo($event); });'); }); it('should allow to cast $event using $any', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE); expect(block).toContain( - '_t1.addEventListener("event", function ($event): any { (ctx).foo(($event as any)); });'); + '_t1.addEventListener("event", ($event): any => { (this).foo(($event as any)); });'); }); it('should detect writes to template variables', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE); - expect(block).toContain( - '_t3.addEventListener("event", function ($event): any { (_t2 = 3); });'); + expect(block).toContain('_t3.addEventListener("event", ($event): any => { (_t2 = 3); });'); }); it('should ignore accesses to $event through `this`', () => { @@ -718,7 +717,7 @@ describe('type check blocks', () => { const block = tcb(TEMPLATE); expect(block).toContain( - '_t1.addEventListener("event", function ($event): any { (ctx).foo(((ctx).$event)); });'); + '_t1.addEventListener("event", ($event): any => { (this).foo(((this).$event)); });'); }); }); @@ -777,12 +776,12 @@ describe('type check blocks', () => { it('should descend into template bodies when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('((ctx).a)'); + expect(block).toContain('((this).a)'); }); it('should not descend into template bodies when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTemplateBodies: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).not.toContain('((ctx).a)'); + expect(block).not.toContain('((this).a)'); }); it('generates a references var when enabled', () => { @@ -802,15 +801,15 @@ describe('type check blocks', () => { it('should include null and undefined when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('_t1.dirInput = (((ctx).a));'); - expect(block).toContain('((ctx).b);'); + expect(block).toContain('_t1.dirInput = (((this).a));'); + expect(block).toContain('((this).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('_t1.dirInput = (((ctx).a)!);'); - expect(block).toContain('((ctx).b)!;'); + expect(block).toContain('_t1.dirInput = (((this).a)!);'); + expect(block).toContain('((this).b)!;'); }); }); @@ -818,8 +817,8 @@ describe('type check blocks', () => { it('should check types of bindings when enabled', () => { const TEMPLATE = `
`; const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('_t1.dirInput = (((ctx).a));'); - expect(block).toContain('((ctx).b);'); + expect(block).toContain('_t1.dirInput = (((this).a));'); + expect(block).toContain('((this).b);'); }); it('should not check types of bindings when disabled', () => { @@ -827,8 +826,8 @@ describe('type check blocks', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfInputBindings: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('_t1.dirInput = ((((ctx).a) as any));'); - expect(block).toContain('(((ctx).b) as any);'); + expect(block).toContain('_t1.dirInput = ((((this).a) as any));'); + expect(block).toContain('(((this).b) as any);'); }); it('should wrap the cast to any in parentheses when required', () => { @@ -836,7 +835,7 @@ describe('type check blocks', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfInputBindings: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('_t1.dirInput = ((((((ctx).a)) === (((ctx).b))) as any));'); + expect(block).toContain('_t1.dirInput = ((((((this).a)) === (((this).b))) as any));'); }); }); @@ -846,18 +845,18 @@ describe('type check blocks', () => { it('should check types of directive outputs when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_t1["outputField"].subscribe(function ($event): any { (ctx).foo($event); });'); + '_t1["outputField"].subscribe(($event): any => { (this).foo($event); });'); expect(block).toContain( - '_t2.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });'); + '_t2.addEventListener("nonDirOutput", ($event): any => { (this).foo($event); });'); }); it('should not check types of directive outputs when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfOutputEvents: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('function ($event: any): any { (ctx).foo($event); }'); + expect(block).toContain('($event: any): any => { (this).foo($event); }'); // Note that DOM events are still checked, that is controlled by `checkTypeOfDomEvents` expect(block).toContain( - 'addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });'); + 'addEventListener("nonDirOutput", ($event): any => { (this).foo($event); });'); }); }); @@ -866,13 +865,13 @@ describe('type check blocks', () => { it('should check types of animation events when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('function ($event: i1.AnimationEvent): any { (ctx).foo($event); }'); + expect(block).toContain('($event: i1.AnimationEvent): any => { (this).foo($event); }'); }); it('should not check types of animation events when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfAnimationEvents: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('function ($event: any): any { (ctx).foo($event); }'); + expect(block).toContain('($event: any): any => { (this).foo($event); }'); }); }); @@ -882,9 +881,9 @@ describe('type check blocks', () => { it('should check types of DOM events when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '_t1["outputField"].subscribe(function ($event): any { (ctx).foo($event); });'); + '_t1["outputField"].subscribe(($event): any => { (this).foo($event); });'); expect(block).toContain( - '_t2.addEventListener("nonDirOutput", function ($event): any { (ctx).foo($event); });'); + '_t2.addEventListener("nonDirOutput", ($event): any => { (this).foo($event); });'); }); it('should not check types of DOM events when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfDomEvents: false}; @@ -892,8 +891,8 @@ describe('type check blocks', () => { // Note that directive outputs are still checked, that is controlled by // `checkTypeOfOutputEvents` expect(block).toContain( - '_t1["outputField"].subscribe(function ($event): any { (ctx).foo($event); });'); - expect(block).toContain('function ($event: any): any { (ctx).foo($event); }'); + '_t1["outputField"].subscribe(($event): any => { (this).foo($event); });'); + expect(block).toContain('($event: any): any => { (this).foo($event); }'); }); }); @@ -986,13 +985,13 @@ describe('type check blocks', () => { it('should check types of pipes when enabled', () => { const block = tcb(TEMPLATE, PIPES); expect(block).toContain('var _pipe1: i0.TestPipe = null!;'); - expect(block).toContain('(_pipe1.transform(((ctx).a), ((ctx).b), ((ctx).c)));'); + expect(block).toContain('(_pipe1.transform(((this).a), ((this).b), ((this).c)));'); }); it('should not check types of pipes when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, checkTypeOfPipes: false}; const block = tcb(TEMPLATE, PIPES, DISABLED_CONFIG); expect(block).toContain('var _pipe1: i0.TestPipe = null!;'); - expect(block).toContain('((_pipe1.transform as any)(((ctx).a), ((ctx).b), ((ctx).c))'); + expect(block).toContain('((_pipe1.transform as any)(((this).a), ((this).b), ((this).c))'); }); }); @@ -1002,19 +1001,19 @@ describe('type check blocks', () => { it('should use undefined for safe navigation operations when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); expect(block).toContain( - '(null as any ? (null as any ? (((ctx).a))!.method : undefined)!() : undefined)'); - expect(block).toContain('(null as any ? (((ctx).a))!.b : undefined)'); - expect(block).toContain('(null as any ? (((ctx).a))![0] : undefined)'); - expect(block).toContain('(null as any ? (((((ctx).a)).optionalMethod))!() : undefined)'); + '(null as any ? (null as any ? (((this).a))!.method : undefined)!() : undefined)'); + expect(block).toContain('(null as any ? (((this).a))!.b : undefined)'); + expect(block).toContain('(null as any ? (((this).a))![0] : undefined)'); + expect(block).toContain('(null as any ? (((((this).a)).optionalMethod))!() : undefined)'); }); it('should use an \'any\' type for safe navigation operations when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, strictSafeNavigationTypes: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('((((((ctx).a))!.method as any) as any)())'); - expect(block).toContain('((((ctx).a))!.b as any)'); - expect(block).toContain('(((((ctx).a))![0] as any)'); - expect(block).toContain('((((((ctx).a)).optionalMethod))!() as any)'); + expect(block).toContain('((((((this).a))!.method as any) as any)())'); + expect(block).toContain('((((this).a))!.b as any)'); + expect(block).toContain('(((((this).a))![0] as any)'); + expect(block).toContain('((((((this).a)).optionalMethod))!() as any)'); }); }); @@ -1023,21 +1022,21 @@ describe('type check blocks', () => { `{{a.method()?.b}} {{a()?.method()}} {{a.method()?.[0]}} {{a.method()?.otherMethod?.()}}`; it('should check the presence of a property/method on the receiver when enabled', () => { const block = tcb(TEMPLATE, DIRECTIVES); - expect(block).toContain('(null as any ? ((((ctx).a)).method())!.b : undefined)'); + expect(block).toContain('(null as any ? ((((this).a)).method())!.b : undefined)'); expect(block).toContain( - '(null as any ? (null as any ? ((ctx).a())!.method : undefined)!() : undefined)'); - expect(block).toContain('(null as any ? ((((ctx).a)).method())![0] : undefined)'); + '(null as any ? (null as any ? ((this).a())!.method : undefined)!() : undefined)'); + expect(block).toContain('(null as any ? ((((this).a)).method())![0] : undefined)'); expect(block).toContain( - '(null as any ? ((null as any ? ((((ctx).a)).method())!.otherMethod : undefined))!() : undefined)'); + '(null as any ? ((null as any ? ((((this).a)).method())!.otherMethod : undefined))!() : undefined)'); }); it('should not check the presence of a property/method on the receiver when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, strictSafeNavigationTypes: false}; const block = tcb(TEMPLATE, DIRECTIVES, DISABLED_CONFIG); - expect(block).toContain('(((((ctx).a)).method()) as any).b'); - expect(block).toContain('(((((ctx).a()) as any).method as any)())'); - expect(block).toContain('(((((ctx).a)).method()) as any)[0]'); - expect(block).toContain('(((((((ctx).a)).method()) as any).otherMethod)!() as any)'); + expect(block).toContain('(((((this).a)).method()) as any).b'); + expect(block).toContain('(((((this).a()) as any).method as any)())'); + expect(block).toContain('(((((this).a)).method()) as any)[0]'); + expect(block).toContain('(((((((this).a)).method()) as any).otherMethod)!() as any)'); }); }); @@ -1046,13 +1045,13 @@ describe('type check blocks', () => { it('should use the generic type of the context when enabled', () => { const block = tcb(TEMPLATE); - expect(block).toContain('function _tcb1(ctx: i0.Test)'); + expect(block).toContain('function _tcb1(this: i0.Test)'); }); it('should use any for the context generic type when disabled', () => { const DISABLED_CONFIG: TypeCheckingConfig = {...BASE_CONFIG, useContextGenericType: false}; const block = tcb(TEMPLATE, undefined, DISABLED_CONFIG); - expect(block).toContain('function _tcb1(ctx: i0.Test)'); + expect(block).toContain('function _tcb1(this: i0.Test)'); }); }); @@ -1076,7 +1075,7 @@ describe('type check blocks', () => { const block = tcb(TEMPLATE, DIRECTIVES, enableChecks); expect(block).toContain( 'var _t1: i0.Dir = null!; ' + - '_t1["some-input.xs"] = (((ctx).foo)); '); + '_t1["some-input.xs"] = (((this).foo)); '); }); it('should assign restricted properties via property access', () => { @@ -1094,7 +1093,7 @@ describe('type check blocks', () => { const block = tcb(TEMPLATE, DIRECTIVES, enableChecks); expect(block).toContain( 'var _t1: i0.Dir = null!; ' + - '_t1.fieldA = (((ctx).foo)); '); + '_t1.fieldA = (((this).foo)); '); }); }); }); @@ -1126,7 +1125,7 @@ describe('type check blocks', () => { const renderedTcb = tcb(template, declarations, {useInlineTypeConstructors: false}); expect(renderedTcb).toContain(`var _t1: i0.Dir = null!;`); - expect(renderedTcb).toContain(`_t1.inputA = (((ctx).foo));`); - expect(renderedTcb).toContain(`_t1.inputB = (((ctx).bar));`); + expect(renderedTcb).toContain(`_t1.inputA = (((this).foo));`); + expect(renderedTcb).toContain(`_t1.inputB = (((this).bar));`); }); }); diff --git a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts index b34d6c3bfc3..d6b0b763b74 100644 --- a/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts +++ b/packages/compiler-cli/src/ngtsc/typecheck/test/type_checker__completion_spec.ts @@ -26,7 +26,7 @@ runInEachFileSystem(() => { if (!ts.isExpressionStatement(node)) { return fail(`Expected a ts.ExpressionStatement`); } - expect(node.expression.getText()).toEqual('ctx.'); + expect(node.expression.getText()).toEqual('this.'); // The position should be between the '.' and a following space. expect(tcbSf.text.slice(positionInFile - 1, positionInFile + 1)).toEqual('. '); });