Skip to content

Commit 6717d8d

Browse files
authored
Merge pull request microsoft#31942 from andrewbranch/bug/30882
Improve error message on indexed access to private members of type parameters
2 parents 917cd6c + 04fbd93 commit 6717d8d

7 files changed

+148
-4
lines changed

src/compiler/checker.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10097,16 +10097,21 @@ namespace ts {
1009710097
return false;
1009810098
}
1009910099

10100-
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
10100+
function getPropertyNameFromIndex(indexType: Type, accessNode: StringLiteral | Identifier | ObjectBindingPattern | ArrayBindingPattern | ComputedPropertyName | NumericLiteral | IndexedAccessTypeNode | ElementAccessExpression | SyntheticExpression | undefined) {
1010110101
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
10102-
const propName = isTypeUsableAsPropertyName(indexType) ?
10102+
return isTypeUsableAsPropertyName(indexType) ?
1010310103
getPropertyNameFromType(indexType) :
1010410104
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
1010510105
getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>accessExpression.argumentExpression).name)) :
1010610106
accessNode && isPropertyName(accessNode) ?
1010710107
// late bound names are handled in the first branch, so here we only need to handle normal names
1010810108
getPropertyNameForPropertyNameNode(accessNode) :
1010910109
undefined;
10110+
}
10111+
10112+
function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) {
10113+
const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
10114+
const propName = getPropertyNameFromIndex(indexType, accessNode);
1011010115
if (propName !== undefined) {
1011110116
const prop = getPropertyOfType(objectType, propName);
1011210117
if (prop) {
@@ -25323,7 +25328,7 @@ namespace ts {
2532325328
forEach(node.types, checkSourceElement);
2532425329
}
2532525330

25326-
function checkIndexedAccessIndexType(type: Type, accessNode: Node) {
25331+
function checkIndexedAccessIndexType(type: Type, accessNode: IndexedAccessTypeNode | ElementAccessExpression) {
2532725332
if (!(type.flags & TypeFlags.IndexedAccess)) {
2532825333
return type;
2532925334
}
@@ -25339,9 +25344,20 @@ namespace ts {
2533925344
}
2534025345
// Check if we're indexing with a numeric type and if either object or index types
2534125346
// is a generic type with a constraint that has a numeric index signature.
25342-
if (getIndexInfoOfType(getApparentType(objectType), IndexKind.Number) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
25347+
const apparentObjectType = getApparentType(objectType);
25348+
if (getIndexInfoOfType(apparentObjectType, IndexKind.Number) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) {
2534325349
return type;
2534425350
}
25351+
if (isGenericObjectType(objectType)) {
25352+
const propertyName = getPropertyNameFromIndex(indexType, accessNode);
25353+
if (propertyName) {
25354+
const propertySymbol = forEachType(apparentObjectType, t => getPropertyOfType(t, propertyName));
25355+
if (propertySymbol && getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.NonPublicAccessibilityModifier) {
25356+
error(accessNode, Diagnostics.Private_or_protected_member_0_cannot_be_accessed_on_a_type_parameter, unescapeLeadingUnderscores(propertyName));
25357+
return errorType;
25358+
}
25359+
}
25360+
}
2534525361
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
2534625362
return errorType;
2534725363
}

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2963,6 +2963,10 @@
29632963
"category": "Error",
29642964
"code": 4104
29652965
},
2966+
"Private or protected member '{0}' cannot be accessed on a type parameter.": {
2967+
"category": "Error",
2968+
"code": 4105
2969+
},
29662970

29672971
"The current host does not support the '{0}' option.": {
29682972
"category": "Error",
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts(9,24): error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
2+
tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts(9,32): error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
3+
tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts(10,27): error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
4+
tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts(11,27): error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
5+
6+
7+
==== tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts (4 errors) ====
8+
class A {
9+
private a: number;
10+
}
11+
12+
class B {
13+
private a: string;
14+
}
15+
16+
type X<T extends A> = [T["a"], (T | B)["a"]];
17+
~~~~~~
18+
!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
19+
~~~~~~~~~~~~
20+
!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
21+
type Y<T extends A | B> = T["a"];
22+
~~~~~~
23+
!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
24+
type Z<T extends A & B> = T["a"];
25+
~~~~~~
26+
!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
27+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [indexedAccessPrivateMemberOfGenericConstraint.ts]
2+
class A {
3+
private a: number;
4+
}
5+
6+
class B {
7+
private a: string;
8+
}
9+
10+
type X<T extends A> = [T["a"], (T | B)["a"]];
11+
type Y<T extends A | B> = T["a"];
12+
type Z<T extends A & B> = T["a"];
13+
14+
15+
//// [indexedAccessPrivateMemberOfGenericConstraint.js]
16+
var A = /** @class */ (function () {
17+
function A() {
18+
}
19+
return A;
20+
}());
21+
var B = /** @class */ (function () {
22+
function B() {
23+
}
24+
return B;
25+
}());
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
=== tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts ===
2+
class A {
3+
>A : Symbol(A, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 0))
4+
5+
private a: number;
6+
>a : Symbol(A.a, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 9))
7+
}
8+
9+
class B {
10+
>B : Symbol(B, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 2, 1))
11+
12+
private a: string;
13+
>a : Symbol(B.a, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 4, 9))
14+
}
15+
16+
type X<T extends A> = [T["a"], (T | B)["a"]];
17+
>X : Symbol(X, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 6, 1))
18+
>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 8, 7))
19+
>A : Symbol(A, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 0))
20+
>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 8, 7))
21+
>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 8, 7))
22+
>B : Symbol(B, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 2, 1))
23+
24+
type Y<T extends A | B> = T["a"];
25+
>Y : Symbol(Y, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 8, 45))
26+
>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 9, 7))
27+
>A : Symbol(A, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 0))
28+
>B : Symbol(B, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 2, 1))
29+
>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 9, 7))
30+
31+
type Z<T extends A & B> = T["a"];
32+
>Z : Symbol(Z, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 9, 33))
33+
>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 10, 7))
34+
>A : Symbol(A, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 0))
35+
>B : Symbol(B, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 2, 1))
36+
>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 10, 7))
37+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
=== tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts ===
2+
class A {
3+
>A : A
4+
5+
private a: number;
6+
>a : number
7+
}
8+
9+
class B {
10+
>B : B
11+
12+
private a: string;
13+
>a : string
14+
}
15+
16+
type X<T extends A> = [T["a"], (T | B)["a"]];
17+
>X : [T["a"], (B | T)["a"]]
18+
19+
type Y<T extends A | B> = T["a"];
20+
>Y : T["a"]
21+
22+
type Z<T extends A & B> = T["a"];
23+
>Z : T["a"]
24+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
class A {
2+
private a: number;
3+
}
4+
5+
class B {
6+
private a: string;
7+
}
8+
9+
type X<T extends A> = [T["a"], (T | B)["a"]];
10+
type Y<T extends A | B> = T["a"];
11+
type Z<T extends A & B> = T["a"];

0 commit comments

Comments
 (0)