Use contextual type to determine 'this' when determining member visibility (#56105)

This commit is contained in:
Ryan Cavanaugh 2024-08-02 15:22:20 -07:00 committed by GitHub
parent 269219f68e
commit aafdfe5b3f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 321 additions and 2 deletions

View File

@ -33667,10 +33667,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function getEnclosingClassFromThisParameter(node: Node): InterfaceType | undefined {
// 'this' type for a node comes from, in priority order...
// 1. The type of a syntactic 'this' parameter in the enclosing function scope
const thisParameter = getThisParameterFromNodeContext(node);
let thisType = thisParameter?.type && getTypeFromTypeNode(thisParameter.type);
if (thisType && thisType.flags & TypeFlags.TypeParameter) {
thisType = getConstraintOfTypeParameter(thisType as TypeParameter);
if (thisType) {
// 2. The constraint of a type parameter used for an explicit 'this' parameter
if (thisType.flags & TypeFlags.TypeParameter) {
thisType = getConstraintOfTypeParameter(thisType as TypeParameter);
}
}
else {
// 3. The 'this' parameter of a contextual type
const thisContainer = getThisContainer(node, /*includeArrowFunctions*/ false, /*includeClassComputedPropertyName*/ false);
if (isFunctionLike(thisContainer)) {
thisType = getContextualThisParameterType(thisContainer);
}
}
if (thisType && getObjectFlags(thisType) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) {
return getTargetType(thisType) as InterfaceType;

View File

@ -0,0 +1,30 @@
protectedAccessThroughContextualThis.ts(13,20): error TS2341: Property 'privat' is private and only accessible within class 'Foo'.
protectedAccessThroughContextualThis.ts(20,20): error TS2341: Property 'privat' is private and only accessible within class 'Foo'.
==== protectedAccessThroughContextualThis.ts (2 errors) ====
class Foo {
protected protec = 'bar';
private privat = '';
copy!: string
constructor() {
bindCopy.call(this)
bindCopy2.call(this)
}
}
function bindCopy(this: Foo) {
this.copy = this.protec; // Should OK
console.log(this.privat); // Should error
~~~~~~
!!! error TS2341: Property 'privat' is private and only accessible within class 'Foo'.
}
type BindingFunction = (this: Foo) => void;
const bindCopy2: BindingFunction = function () {
this.copy = this.protec; // Should OK
console.log(this.privat); // Should error
~~~~~~
!!! error TS2341: Property 'privat' is private and only accessible within class 'Foo'.
}

View File

@ -0,0 +1,44 @@
//// [tests/cases/compiler/protectedAccessThroughContextualThis.ts] ////
//// [protectedAccessThroughContextualThis.ts]
class Foo {
protected protec = 'bar';
private privat = '';
copy!: string
constructor() {
bindCopy.call(this)
bindCopy2.call(this)
}
}
function bindCopy(this: Foo) {
this.copy = this.protec; // Should OK
console.log(this.privat); // Should error
}
type BindingFunction = (this: Foo) => void;
const bindCopy2: BindingFunction = function () {
this.copy = this.protec; // Should OK
console.log(this.privat); // Should error
}
//// [protectedAccessThroughContextualThis.js]
"use strict";
var Foo = /** @class */ (function () {
function Foo() {
this.protec = 'bar';
this.privat = '';
bindCopy.call(this);
bindCopy2.call(this);
}
return Foo;
}());
function bindCopy() {
this.copy = this.protec; // Should OK
console.log(this.privat); // Should error
}
var bindCopy2 = function () {
this.copy = this.protec; // Should OK
console.log(this.privat); // Should error
};

View File

@ -0,0 +1,77 @@
//// [tests/cases/compiler/protectedAccessThroughContextualThis.ts] ////
=== protectedAccessThroughContextualThis.ts ===
class Foo {
>Foo : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
protected protec = 'bar';
>protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
private privat = '';
>privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
copy!: string
>copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
constructor() {
bindCopy.call(this)
>bindCopy.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
>bindCopy : Symbol(bindCopy, Decl(protectedAccessThroughContextualThis.ts, 8, 1))
>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
>this : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
bindCopy2.call(this)
>bindCopy2.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
>bindCopy2 : Symbol(bindCopy2, Decl(protectedAccessThroughContextualThis.ts, 17, 5))
>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
>this : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
}
}
function bindCopy(this: Foo) {
>bindCopy : Symbol(bindCopy, Decl(protectedAccessThroughContextualThis.ts, 8, 1))
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 10, 18))
>Foo : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
this.copy = this.protec; // Should OK
>this.copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 10, 18))
>copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
>this.protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 10, 18))
>protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
console.log(this.privat); // Should error
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this.privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 10, 18))
>privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
}
type BindingFunction = (this: Foo) => void;
>BindingFunction : Symbol(BindingFunction, Decl(protectedAccessThroughContextualThis.ts, 13, 1))
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 15, 24))
>Foo : Symbol(Foo, Decl(protectedAccessThroughContextualThis.ts, 0, 0))
const bindCopy2: BindingFunction = function () {
>bindCopy2 : Symbol(bindCopy2, Decl(protectedAccessThroughContextualThis.ts, 17, 5))
>BindingFunction : Symbol(BindingFunction, Decl(protectedAccessThroughContextualThis.ts, 13, 1))
this.copy = this.protec; // Should OK
>this.copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 15, 24))
>copy : Symbol(Foo.copy, Decl(protectedAccessThroughContextualThis.ts, 2, 22))
>this.protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 15, 24))
>protec : Symbol(Foo.protec, Decl(protectedAccessThroughContextualThis.ts, 0, 11))
console.log(this.privat); // Should error
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>this.privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
>this : Symbol(this, Decl(protectedAccessThroughContextualThis.ts, 15, 24))
>privat : Symbol(Foo.privat, Decl(protectedAccessThroughContextualThis.ts, 1, 27))
}

View File

@ -0,0 +1,133 @@
//// [tests/cases/compiler/protectedAccessThroughContextualThis.ts] ////
=== protectedAccessThroughContextualThis.ts ===
class Foo {
>Foo : Foo
> : ^^^
protected protec = 'bar';
>protec : string
> : ^^^^^^
>'bar' : "bar"
> : ^^^^^
private privat = '';
>privat : string
> : ^^^^^^
>'' : ""
> : ^^
copy!: string
>copy : string
> : ^^^^^^
constructor() {
bindCopy.call(this)
>bindCopy.call(this) : void
> : ^^^^
>bindCopy.call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
>bindCopy : (this: Foo) => void
> : ^ ^^ ^^^^^^^^^
>call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
>this : this
> : ^^^^
bindCopy2.call(this)
>bindCopy2.call(this) : void
> : ^^^^
>bindCopy2.call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
>bindCopy2 : BindingFunction
> : ^^^^^^^^^^^^^^^
>call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
> : ^ ^^ ^^^^^^^^^ ^^ ^^ ^^ ^^ ^^ ^^^^^ ^^ ^^^^^
>this : this
> : ^^^^
}
}
function bindCopy(this: Foo) {
>bindCopy : (this: Foo) => void
> : ^ ^^ ^^^^^^^^^
>this : Foo
> : ^^^
this.copy = this.protec; // Should OK
>this.copy = this.protec : string
> : ^^^^^^
>this.copy : string
> : ^^^^^^
>this : Foo
> : ^^^
>copy : string
> : ^^^^^^
>this.protec : string
> : ^^^^^^
>this : Foo
> : ^^^
>protec : string
> : ^^^^^^
console.log(this.privat); // Should error
>console.log(this.privat) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>this.privat : string
> : ^^^^^^
>this : Foo
> : ^^^
>privat : string
> : ^^^^^^
}
type BindingFunction = (this: Foo) => void;
>BindingFunction : BindingFunction
> : ^^^^^^^^^^^^^^^
>this : Foo
> : ^^^
const bindCopy2: BindingFunction = function () {
>bindCopy2 : BindingFunction
> : ^^^^^^^^^^^^^^^
>function () { this.copy = this.protec; // Should OK console.log(this.privat); // Should error} : (this: Foo) => void
> : ^ ^^ ^^^^^^^^^
this.copy = this.protec; // Should OK
>this.copy = this.protec : string
> : ^^^^^^
>this.copy : string
> : ^^^^^^
>this : Foo
> : ^^^
>copy : string
> : ^^^^^^
>this.protec : string
> : ^^^^^^
>this : Foo
> : ^^^
>protec : string
> : ^^^^^^
console.log(this.privat); // Should error
>console.log(this.privat) : void
> : ^^^^
>console.log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>console : Console
> : ^^^^^^^
>log : (...data: any[]) => void
> : ^^^^ ^^ ^^^^^
>this.privat : string
> : ^^^^^^
>this : Foo
> : ^^^
>privat : string
> : ^^^^^^
}

View File

@ -0,0 +1,23 @@
// @strict: true
class Foo {
protected protec = 'bar';
private privat = '';
copy!: string
constructor() {
bindCopy.call(this)
bindCopy2.call(this)
}
}
function bindCopy(this: Foo) {
this.copy = this.protec; // Should OK
console.log(this.privat); // Should error
}
type BindingFunction = (this: Foo) => void;
const bindCopy2: BindingFunction = function () {
this.copy = this.protec; // Should OK
console.log(this.privat); // Should error
}