Skip to content

Commit aafdfe5

Browse files
Use contextual type to determine 'this' when determining member visibility (#56105)
1 parent 269219f commit aafdfe5

6 files changed

+321
-2
lines changed

src/compiler/checker.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33667,10 +33667,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3366733667
}
3366833668

3366933669
function getEnclosingClassFromThisParameter(node: Node): InterfaceType | undefined {
33670+
// 'this' type for a node comes from, in priority order...
33671+
// 1. The type of a syntactic 'this' parameter in the enclosing function scope
3367033672
const thisParameter = getThisParameterFromNodeContext(node);
3367133673
let thisType = thisParameter?.type && getTypeFromTypeNode(thisParameter.type);
33672-
if (thisType && thisType.flags & TypeFlags.TypeParameter) {
33673-
thisType = getConstraintOfTypeParameter(thisType as TypeParameter);
33674+
if (thisType) {
33675+
// 2. The constraint of a type parameter used for an explicit 'this' parameter
33676+
if (thisType.flags & TypeFlags.TypeParameter) {
33677+
thisType = getConstraintOfTypeParameter(thisType as TypeParameter);
33678+
}
33679+
}
33680+
else {
33681+
// 3. The 'this' parameter of a contextual type
33682+
const thisContainer = getThisContainer(node, /*includeArrowFunctions*/ false, /*includeClassComputedPropertyName*/ false);
33683+
if (isFunctionLike(thisContainer)) {
33684+
thisType = getContextualThisParameterType(thisContainer);
33685+
}
3367433686
}
3367533687
if (thisType && getObjectFlags(thisType) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) {
3367633688
return getTargetType(thisType) as InterfaceType;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
protectedAccessThroughContextualThis.ts(13,20): error TS2341: Property 'privat' is private and only accessible within class 'Foo'.
2+
protectedAccessThroughContextualThis.ts(20,20): error TS2341: Property 'privat' is private and only accessible within class 'Foo'.
3+
4+
5+
==== protectedAccessThroughContextualThis.ts (2 errors) ====
6+
class Foo {
7+
protected protec = 'bar';
8+
private privat = '';
9+
copy!: string
10+
constructor() {
11+
bindCopy.call(this)
12+
bindCopy2.call(this)
13+
}
14+
}
15+
16+
function bindCopy(this: Foo) {
17+
this.copy = this.protec; // Should OK
18+
console.log(this.privat); // Should error
19+
~~~~~~
20+
!!! error TS2341: Property 'privat' is private and only accessible within class 'Foo'.
21+
}
22+
23+
type BindingFunction = (this: Foo) => void;
24+
25+
const bindCopy2: BindingFunction = function () {
26+
this.copy = this.protec; // Should OK
27+
console.log(this.privat); // Should error
28+
~~~~~~
29+
!!! error TS2341: Property 'privat' is private and only accessible within class 'Foo'.
30+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//// [tests/cases/compiler/protectedAccessThroughContextualThis.ts] ////
2+
3+
//// [protectedAccessThroughContextualThis.ts]
4+
class Foo {
5+
protected protec = 'bar';
6+
private privat = '';
7+
copy!: string
8+
constructor() {
9+
bindCopy.call(this)
10+
bindCopy2.call(this)
11+
}
12+
}
13+
14+
function bindCopy(this: Foo) {
15+
this.copy = this.protec; // Should OK
16+
console.log(this.privat); // Should error
17+
}
18+
19+
type BindingFunction = (this: Foo) => void;
20+
21+
const bindCopy2: BindingFunction = function () {
22+
this.copy = this.protec; // Should OK
23+
console.log(this.privat); // Should error
24+
}
25+
26+
//// [protectedAccessThroughContextualThis.js]
27+
"use strict";
28+
var Foo = /** @class */ (function () {
29+
function Foo() {
30+
this.protec = 'bar';
31+
this.privat = '';
32+
bindCopy.call(this);
33+
bindCopy2.call(this);
34+
}
35+
return Foo;
36+
}());
37+
function bindCopy() {
38+
this.copy = this.protec; // Should OK
39+
console.log(this.privat); // Should error
40+
}
41+
var bindCopy2 = function () {
42+
this.copy = this.protec; // Should OK
43+
console.log(this.privat); // Should error
44+
};
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
//// [tests/cases/compiler/protectedAccessThroughContextualThis.ts] ////
2+
3+
=== protectedAccessThroughContextualThis.ts ===
4+
class Foo {
5+
>Foo : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
6+
7+
protected protec = 'bar';
8+
>protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
9+
10+
private privat = '';
11+
>privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
12+
13+
copy!: string
14+
>copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
15+
16+
constructor() {
17+
bindCopy.call(this)
18+
>bindCopy.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
19+
>bindCopy : Symbol(bindCopy, Decl(protectedAccessThroughContextualThis.ts, 8, 1))
20+
>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
21+
>this : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
22+
23+
bindCopy2.call(this)
24+
>bindCopy2.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
25+
>bindCopy2 : Symbol(bindCopy2, Decl(protectedAccessThroughContextualThis.ts, 17, 5))
26+
>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
27+
>this : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
28+
}
29+
}
30+
31+
function bindCopy(this: Foo) {
32+
>bindCopy : Symbol(bindCopy, Decl(protectedAccessThroughContextualThis.ts, 8, 1))
33+
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 10, 18))
34+
>Foo : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
35+
36+
this.copy = this.protec; // Should OK
37+
>this.copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
38+
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 10, 18))
39+
>copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
40+
>this.protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
41+
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 10, 18))
42+
>protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
43+
44+
console.log(this.privat); // Should error
45+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
46+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
47+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
48+
>this.privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
49+
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 10, 18))
50+
>privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
51+
}
52+
53+
type BindingFunction = (this: Foo) => void;
54+
>BindingFunction : Symbol(BindingFunction, Decl(protectedAccessThroughContextualThis.ts, 13, 1))
55+
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 15, 24))
56+
>Foo : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
57+
58+
const bindCopy2: BindingFunction = function () {
59+
>bindCopy2 : Symbol(bindCopy2, Decl(protectedAccessThroughContextualThis.ts, 17, 5))
60+
>BindingFunction : Symbol(BindingFunction, Decl(protectedAccessThroughContextualThis.ts, 13, 1))
61+
62+
this.copy = this.protec; // Should OK
63+
>this.copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
64+
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 15, 24))
65+
>copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
66+
>this.protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
67+
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 15, 24))
68+
>protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
69+
70+
console.log(this.privat); // Should error
71+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
72+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
73+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
74+
>this.privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
75+
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 15, 24))
76+
>privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
77+
}
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
//// [tests/cases/compiler/protectedAccessThroughContextualThis.ts] ////
2+
3+
=== protectedAccessThroughContextualThis.ts ===
4+
class Foo {
5+
>Foo : Foo
6+
> : ^^^
7+
8+
protected protec = 'bar';
9+
>protec : string
10+
> : ^^^^^^
11+
>'bar' : "bar"
12+
> : ^^^^^
13+
14+
private privat = '';
15+
>privat : string
16+
> : ^^^^^^
17+
>'' : ""
18+
> : ^^
19+
20+
copy!: string
21+
>copy : string
22+
> : ^^^^^^
23+
24+
constructor() {
25+
bindCopy.call(this)
26+
>bindCopy.call(this) : void
27+
> : ^^^^
28+
>bindCopy.call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
29+
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
30+
>bindCopy : (this: Foo) => void
31+
> : ^ ^^ ^^^^^^^^^
32+
>call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
33+
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
34+
>this : this
35+
> : ^^^^
36+
37+
bindCopy2.call(this)
38+
>bindCopy2.call(this) : void
39+
> : ^^^^
40+
>bindCopy2.call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
41+
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
42+
>bindCopy2 : BindingFunction
43+
> : ^^^^^^^^^^^^^^^
44+
>call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
45+
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
46+
>this : this
47+
> : ^^^^
48+
}
49+
}
50+
51+
function bindCopy(this: Foo) {
52+
>bindCopy : (this: Foo) => void
53+
> : ^ ^^ ^^^^^^^^^
54+
>this : Foo
55+
> : ^^^
56+
57+
this.copy = this.protec; // Should OK
58+
>this.copy = this.protec : string
59+
> : ^^^^^^
60+
>this.copy : string
61+
> : ^^^^^^
62+
>this : Foo
63+
> : ^^^
64+
>copy : string
65+
> : ^^^^^^
66+
>this.protec : string
67+
> : ^^^^^^
68+
>this : Foo
69+
> : ^^^
70+
>protec : string
71+
> : ^^^^^^
72+
73+
console.log(this.privat); // Should error
74+
>console.log(this.privat) : void
75+
> : ^^^^
76+
>console.log : (...data: any[]) => void
77+
> : ^^^^ ^^ ^^^^^
78+
>console : Console
79+
> : ^^^^^^^
80+
>log : (...data: any[]) => void
81+
> : ^^^^ ^^ ^^^^^
82+
>this.privat : string
83+
> : ^^^^^^
84+
>this : Foo
85+
> : ^^^
86+
>privat : string
87+
> : ^^^^^^
88+
}
89+
90+
type BindingFunction = (this: Foo) => void;
91+
>BindingFunction : BindingFunction
92+
> : ^^^^^^^^^^^^^^^
93+
>this : Foo
94+
> : ^^^
95+
96+
const bindCopy2: BindingFunction = function () {
97+
>bindCopy2 : BindingFunction
98+
> : ^^^^^^^^^^^^^^^
99+
>function () { this.copy = this.protec; // Should OK console.log(this.privat); // Should error} : (this: Foo) => void
100+
> : ^ ^^ ^^^^^^^^^
101+
102+
this.copy = this.protec; // Should OK
103+
>this.copy = this.protec : string
104+
> : ^^^^^^
105+
>this.copy : string
106+
> : ^^^^^^
107+
>this : Foo
108+
> : ^^^
109+
>copy : string
110+
> : ^^^^^^
111+
>this.protec : string
112+
> : ^^^^^^
113+
>this : Foo
114+
> : ^^^
115+
>protec : string
116+
> : ^^^^^^
117+
118+
console.log(this.privat); // Should error
119+
>console.log(this.privat) : void
120+
> : ^^^^
121+
>console.log : (...data: any[]) => void
122+
> : ^^^^ ^^ ^^^^^
123+
>console : Console
124+
> : ^^^^^^^
125+
>log : (...data: any[]) => void
126+
> : ^^^^ ^^ ^^^^^
127+
>this.privat : string
128+
> : ^^^^^^
129+
>this : Foo
130+
> : ^^^
131+
>privat : string
132+
> : ^^^^^^
133+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// @strict: true
2+
3+
class Foo {
4+
protected protec = 'bar';
5+
private privat = '';
6+
copy!: string
7+
constructor() {
8+
bindCopy.call(this)
9+
bindCopy2.call(this)
10+
}
11+
}
12+
13+
function bindCopy(this: Foo) {
14+
this.copy = this.protec; // Should OK
15+
console.log(this.privat); // Should error
16+
}
17+
18+
type BindingFunction = (this: Foo) => void;
19+
20+
const bindCopy2: BindingFunction = function () {
21+
this.copy = this.protec; // Should OK
22+
console.log(this.privat); // Should error
23+
}

0 commit comments

Comments
 (0)