Merge pull request #31942 from andrewbranch/bug/30882
Improve error message on indexed access to private members of type parameters
This commit is contained in:
commit
6717d8d928
7 changed files with 148 additions and 4 deletions
|
@ -10097,9 +10097,9 @@ namespace ts {
|
||||||
return false;
|
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 accessExpression = accessNode && accessNode.kind === SyntaxKind.ElementAccessExpression ? accessNode : undefined;
|
||||||
const propName = isTypeUsableAsPropertyName(indexType) ?
|
return isTypeUsableAsPropertyName(indexType) ?
|
||||||
getPropertyNameFromType(indexType) :
|
getPropertyNameFromType(indexType) :
|
||||||
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
|
accessExpression && checkThatExpressionIsProperSymbolReference(accessExpression.argumentExpression, indexType, /*reportError*/ false) ?
|
||||||
getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>accessExpression.argumentExpression).name)) :
|
getPropertyNameForKnownSymbolName(idText((<PropertyAccessExpression>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
|
// late bound names are handled in the first branch, so here we only need to handle normal names
|
||||||
getPropertyNameForPropertyNameNode(accessNode) :
|
getPropertyNameForPropertyNameNode(accessNode) :
|
||||||
undefined;
|
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) {
|
if (propName !== undefined) {
|
||||||
const prop = getPropertyOfType(objectType, propName);
|
const prop = getPropertyOfType(objectType, propName);
|
||||||
if (prop) {
|
if (prop) {
|
||||||
|
@ -25323,7 +25328,7 @@ namespace ts {
|
||||||
forEach(node.types, checkSourceElement);
|
forEach(node.types, checkSourceElement);
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkIndexedAccessIndexType(type: Type, accessNode: Node) {
|
function checkIndexedAccessIndexType(type: Type, accessNode: IndexedAccessTypeNode | ElementAccessExpression) {
|
||||||
if (!(type.flags & TypeFlags.IndexedAccess)) {
|
if (!(type.flags & TypeFlags.IndexedAccess)) {
|
||||||
return type;
|
return type;
|
||||||
}
|
}
|
||||||
|
@ -25339,9 +25344,20 @@ namespace ts {
|
||||||
}
|
}
|
||||||
// Check if we're indexing with a numeric type and if either object or index types
|
// 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.
|
// 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;
|
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));
|
error(accessNode, Diagnostics.Type_0_cannot_be_used_to_index_type_1, typeToString(indexType), typeToString(objectType));
|
||||||
return errorType;
|
return errorType;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2963,6 +2963,10 @@
|
||||||
"category": "Error",
|
"category": "Error",
|
||||||
"code": 4104
|
"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.": {
|
"The current host does not support the '{0}' option.": {
|
||||||
"category": "Error",
|
"category": "Error",
|
||||||
|
|
|
@ -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 extends A> = [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 extends A | B> = T["a"];
|
||||||
|
~~~~~~
|
||||||
|
!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
|
||||||
|
type Z<T extends A & B> = T["a"];
|
||||||
|
~~~~~~
|
||||||
|
!!! error TS4105: Private or protected member 'a' cannot be accessed on a type parameter.
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
//// [indexedAccessPrivateMemberOfGenericConstraint.ts]
|
||||||
|
class A {
|
||||||
|
private a: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
private a: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type X<T extends A> = [T["a"], (T | B)["a"]];
|
||||||
|
type Y<T extends A | B> = T["a"];
|
||||||
|
type Z<T extends A & B> = T["a"];
|
||||||
|
|
||||||
|
|
||||||
|
//// [indexedAccessPrivateMemberOfGenericConstraint.js]
|
||||||
|
var A = /** @class */ (function () {
|
||||||
|
function A() {
|
||||||
|
}
|
||||||
|
return A;
|
||||||
|
}());
|
||||||
|
var B = /** @class */ (function () {
|
||||||
|
function B() {
|
||||||
|
}
|
||||||
|
return B;
|
||||||
|
}());
|
|
@ -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 extends A> = [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 extends A | B> = 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 extends A & B> = 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))
|
||||||
|
|
|
@ -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 extends A> = [T["a"], (T | B)["a"]];
|
||||||
|
>X : [T["a"], (B | T)["a"]]
|
||||||
|
|
||||||
|
type Y<T extends A | B> = T["a"];
|
||||||
|
>Y : T["a"]
|
||||||
|
|
||||||
|
type Z<T extends A & B> = T["a"];
|
||||||
|
>Z : T["a"]
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
class A {
|
||||||
|
private a: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
class B {
|
||||||
|
private a: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type X<T extends A> = [T["a"], (T | B)["a"]];
|
||||||
|
type Y<T extends A | B> = T["a"];
|
||||||
|
type Z<T extends A & B> = T["a"];
|
Loading…
Reference in a new issue