diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 9cb08b3e44..a3abe1dc5b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -10097,9 +10097,9 @@ namespace ts { return false; } - function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) { + function getPropertyNameFromIndex(indexType: Type, accessNode: StringLiteral | Identifier | ObjectBindingPattern | ArrayBindingPattern | ComputedPropertyName | NumericLiteral | IndexedAccessTypeNode | ElementAccessExpression | SyntheticExpression | undefined) { const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; - const propName = isTypeUsableAsPropertyName(indexType) ? + return isTypeUsableAsPropertyName(indexType) ? getPropertyNameFromType(indexType) : accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ? getPropertyNameForKnownSymbolName(idText((accessExpression.argumentExpression).name)) : @@ -10107,6 +10107,11 @@ namespace ts { // late bound names are handled in the first branch, so here we only need to handle normal names getPropertyNameForPropertyNameNode(accessNode) : undefined; + } + + function getPropertyTypeForIndexType(originalObjectType: Type, objectType: Type, indexType: Type, fullIndexType: Type, suppressNoImplicitAnyError: boolean, accessNode: ElementAccessExpression | IndexedAccessTypeNode | PropertyName | BindingName | SyntheticExpression | undefined, accessFlags: AccessFlags) { + const accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined; + const propName = getPropertyNameFromIndex(indexType, accessNode); if (propName !== undefined) { const prop = getPropertyOfType(objectType, propName); if (prop) { @@ -25323,7 +25328,7 @@ namespace ts { forEach(node.types, checkSourceElement); } - function checkIndexedAccessIndexType(type: Type, accessNode: Node) { + function checkIndexedAccessIndexType(type: Type, accessNode: IndexedAccessTypeNode | ElementAccessExpression) { if (!(type.flags & TypeFlags.IndexedAccess)) { return type; } @@ -25339,9 +25344,20 @@ namespace ts { } // Check if we're indexing with a numeric type and if either object or index types // is a generic type with a constraint that has a numeric index signature. - if (getIndexInfoOfType(getApparentType(objectType), IndexKind.Number) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { + const apparentObjectType = getApparentType(objectType); + if (getIndexInfoOfType(apparentObjectType, IndexKind.Number) && isTypeAssignableToKind(indexType, TypeFlags.NumberLike)) { return type; } + if (isGenericObjectType(objectType)) { + const propertyName = getPropertyNameFromIndex(indexType, accessNode); + if (propertyName) { + const propertySymbol = forEachType(apparentObjectType, t => getPropertyOfType(t, propertyName)); + if (propertySymbol && getDeclarationModifierFlagsFromSymbol(propertySymbol) & ModifierFlags.NonPublicAccessibilityModifier) { + error(accessNode, Diagnostics.Private_or_protected_member_0_cannot_be_accessed_on_a_type_parameter, unescapeLeadingUnderscores(propertyName)); + return errorType; + } + } + } error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType)); return errorType; } diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 529484956e..2ea72a2449 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2963,6 +2963,10 @@ "category": "Error", "code": 4104 }, + "Private or protected member '{0}' cannot be accessed on a type parameter.": { + "category": "Error", + "code": 4105 + }, "The current host does not support the '{0}' option.": { "category": "Error", diff --git a/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.errors.txt b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.errors.txt new file mode 100644 index 0000000000..56bf43e77a --- /dev/null +++ b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.errors.txt @@ -0,0 +1,27 @@ +tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts(9,24): error TS4105: Private or protected member 'a' cannot be accessed on a type parameter. +tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts(9,32): error TS4105: Private or protected member 'a' cannot be accessed on a type parameter. +tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts(10,27): error TS4105: Private or protected member 'a' cannot be accessed on a type parameter. +tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts(11,27): error TS4105: Private or protected member 'a' cannot be accessed on a type parameter. + + +==== tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts (4 errors) ==== + class A { + private a: number; + } + + class B { + private a: string; + } + + type X = [T["a"], (T | B)["a"]]; + ~~~~~~ +!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter. + ~~~~~~~~~~~~ +!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter. + type Y = T["a"]; + ~~~~~~ +!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter. + type Z = T["a"]; + ~~~~~~ +!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter. + \ No newline at end of file diff --git a/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.js b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.js new file mode 100644 index 0000000000..5d2bfc5891 --- /dev/null +++ b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.js @@ -0,0 +1,25 @@ +//// [indexedAccessPrivateMemberOfGenericConstraint.ts] +class A { + private a: number; +} + +class B { + private a: string; +} + +type X = [T["a"], (T | B)["a"]]; +type Y = T["a"]; +type Z = T["a"]; + + +//// [indexedAccessPrivateMemberOfGenericConstraint.js] +var A = /** @class */ (function () { + function A() { + } + return A; +}()); +var B = /** @class */ (function () { + function B() { + } + return B; +}()); diff --git a/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.symbols b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.symbols new file mode 100644 index 0000000000..69c433b16d --- /dev/null +++ b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.symbols @@ -0,0 +1,37 @@ +=== tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts === +class A { +>A : Symbol(A, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 0)) + + private a: number; +>a : Symbol(A.a, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 9)) +} + +class B { +>B : Symbol(B, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 2, 1)) + + private a: string; +>a : Symbol(B.a, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 4, 9)) +} + +type X = [T["a"], (T | B)["a"]]; +>X : Symbol(X, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 6, 1)) +>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 8, 7)) +>A : Symbol(A, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 0)) +>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 8, 7)) +>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 8, 7)) +>B : Symbol(B, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 2, 1)) + +type Y = T["a"]; +>Y : Symbol(Y, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 8, 45)) +>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 9, 7)) +>A : Symbol(A, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 0)) +>B : Symbol(B, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 2, 1)) +>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 9, 7)) + +type Z = T["a"]; +>Z : Symbol(Z, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 9, 33)) +>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 10, 7)) +>A : Symbol(A, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 0, 0)) +>B : Symbol(B, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 2, 1)) +>T : Symbol(T, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 10, 7)) + diff --git a/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.types b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.types new file mode 100644 index 0000000000..abdbaa8486 --- /dev/null +++ b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.types @@ -0,0 +1,24 @@ +=== tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts === +class A { +>A : A + + private a: number; +>a : number +} + +class B { +>B : B + + private a: string; +>a : string +} + +type X = [T["a"], (T | B)["a"]]; +>X : [T["a"], (B | T)["a"]] + +type Y = T["a"]; +>Y : T["a"] + +type Z = T["a"]; +>Z : T["a"] + diff --git a/tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts b/tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts new file mode 100644 index 0000000000..a2fa8f4a46 --- /dev/null +++ b/tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts @@ -0,0 +1,11 @@ +class A { + private a: number; +} + +class B { + private a: string; +} + +type X = [T["a"], (T | B)["a"]]; +type Y = T["a"]; +type Z = T["a"];