diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 65076aefa21..4f8f09e51ec 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9987,8 +9987,8 @@ namespace ts { }); } - function getLiteralTypeFromProperty(prop: Symbol, include: TypeFlags, includeNonPublic?: boolean) { - if (includeNonPublic || !(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) { + function getLiteralTypeFromProperty(prop: Symbol, include: TypeFlags) { + if (!(getDeclarationModifierFlagsFromSymbol(prop) & ModifierFlags.NonPublicAccessibilityModifier)) { let type = getLateBoundSymbol(prop).nameType; if (!type && !isKnownSymbol(prop)) { if (prop.escapedName === InternalSymbolName.Default) { @@ -10006,8 +10006,8 @@ namespace ts { return neverType; } - function getLiteralTypeFromProperties(type: Type, include: TypeFlags, includeNonPublic?: boolean) { - return getUnionType(map(getPropertiesOfType(type), p => getLiteralTypeFromProperty(p, include, includeNonPublic))); + function getLiteralTypeFromProperties(type: Type, include: TypeFlags) { + return getUnionType(map(getPropertiesOfType(type), p => getLiteralTypeFromProperty(p, include))); } function getNonEnumNumberIndexInfo(type: Type) { @@ -10023,10 +10023,10 @@ namespace ts { type === wildcardType ? wildcardType : type.flags & TypeFlags.Unknown ? neverType : type.flags & (TypeFlags.Any | TypeFlags.Never) ? keyofConstraintType : - stringsOnly ? !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromProperties(type, TypeFlags.StringLiteral, /*includeNonPublic*/ true) : - !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringType, numberType, getLiteralTypeFromProperties(type, TypeFlags.UniqueESSymbol, /*includeNonPublic*/ true)]) : - getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol, /*includeNonPublic*/ true)]) : - getLiteralTypeFromProperties(type, TypeFlags.StringOrNumberLiteralOrUnique, /*includeNonPublic*/ true); + stringsOnly ? !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? stringType : getLiteralTypeFromProperties(type, TypeFlags.StringLiteral) : + !noIndexSignatures && getIndexInfoOfType(type, IndexKind.String) ? getUnionType([stringType, numberType, getLiteralTypeFromProperties(type, TypeFlags.UniqueESSymbol)]) : + getNonEnumNumberIndexInfo(type) ? getUnionType([numberType, getLiteralTypeFromProperties(type, TypeFlags.StringLiteral | TypeFlags.UniqueESSymbol)]) : + getLiteralTypeFromProperties(type, TypeFlags.StringOrNumberLiteralOrUnique); } function getExtractStringType(type: Type) { @@ -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) { @@ -25292,7 +25297,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; } @@ -25308,9 +25313,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 b209a878804..061c242e26b 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 00000000000..56bf43e77ac --- /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 index bd10676fab0..5d2bfc5891d 100644 --- a/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.js +++ b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.js @@ -7,10 +7,9 @@ class B { private a: string; } -type X = T["a"]; +type X = [T["a"], (T | B)["a"]]; type Y = T["a"]; type Z = T["a"]; - //// [indexedAccessPrivateMemberOfGenericConstraint.js] diff --git a/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.symbols b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.symbols index 9a57021cff4..69c433b16d5 100644 --- a/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.symbols +++ b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.symbols @@ -13,14 +13,16 @@ class B { >a : Symbol(B.a, Decl(indexedAccessPrivateMemberOfGenericConstraint.ts, 4, 9)) } -type X = T["a"]; +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, 29)) +>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)) @@ -33,4 +35,3 @@ type Z = T["a"]; >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 index 3e1e6684993..abdbaa84862 100644 --- a/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.types +++ b/tests/baselines/reference/indexedAccessPrivateMemberOfGenericConstraint.types @@ -13,8 +13,8 @@ class B { >a : string } -type X = T["a"]; ->X : T["a"] +type X = [T["a"], (T | B)["a"]]; +>X : [T["a"], (B | T)["a"]] type Y = T["a"]; >Y : T["a"] @@ -22,4 +22,3 @@ type Y = T["a"]; type Z = T["a"]; >Z : T["a"] - diff --git a/tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts b/tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts index 77823cccef1..a2fa8f4a469 100644 --- a/tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts +++ b/tests/cases/compiler/indexedAccessPrivateMemberOfGenericConstraint.ts @@ -6,7 +6,6 @@ class B { private a: string; } -type X = T["a"]; +type X = [T["a"], (T | B)["a"]]; type Y = T["a"]; type Z = T["a"]; -