Fix crash when this is typed as a generic T with no type constraints (#47991)

* fix this type validations for protected fields

* update previous baselines

* add new tests and baselines

* ClassOrInterface can be returned as enclosingClass

* inline function code and remove unnecessary blank lines

* refactor enclosingClass checks
This commit is contained in:
jihndai
2022-03-04 21:08:44 -04:00
committed by GitHub
parent 82fc9b19ba
commit 857889a4e6
7 changed files with 1177 additions and 12 deletions

View File

@@ -20644,7 +20644,7 @@ namespace ts {
// Return true if the given class derives from each of the declaring classes of the protected
// constituents of the given property.
function isClassDerivedFromDeclaringClasses(checkClass: Type, prop: Symbol, writing: boolean) {
function isClassDerivedFromDeclaringClasses<T extends Type>(checkClass: T, prop: Symbol, writing: boolean) {
return forEachProperty(prop, p => getDeclarationModifierFlagsFromSymbol(p, writing) & ModifierFlags.Protected ?
!hasBaseType(checkClass, getDeclaringClass(p)) : false) ? undefined : checkClass;
}
@@ -28396,14 +28396,15 @@ namespace ts {
// of the property as base classes
let enclosingClass = forEachEnclosingClass(location, enclosingDeclaration => {
const enclosingClass = getDeclaredTypeOfSymbol(getSymbolOfNode(enclosingDeclaration)!) as InterfaceType;
return isClassDerivedFromDeclaringClasses(enclosingClass, prop, writing) ? enclosingClass : undefined;
return isClassDerivedFromDeclaringClasses(enclosingClass, prop, writing);
});
// A protected property is accessible if the property is within the declaring class or classes derived from it
if (!enclosingClass) {
// allow PropertyAccessibility if context is in function with this parameter
// static member access is disallow
let thisParameter: ParameterDeclaration | undefined;
if (flags & ModifierFlags.Static || !(thisParameter = getThisParameterFromNodeContext(location)) || !thisParameter.type) {
// static member access is disallowed
enclosingClass = getEnclosingClassFromThisParameter(location);
enclosingClass = enclosingClass && isClassDerivedFromDeclaringClasses(enclosingClass, prop, writing);
if (flags & ModifierFlags.Static || !enclosingClass) {
if (errorNode) {
error(errorNode,
Diagnostics.Property_0_is_protected_and_only_accessible_within_class_1_and_its_subclasses,
@@ -28412,9 +28413,6 @@ namespace ts {
}
return false;
}
const thisType = getTypeFromTypeNode(thisParameter.type);
enclosingClass = (((thisType.flags & TypeFlags.TypeParameter) ? getConstraintOfTypeParameter(thisType as TypeParameter) : thisType) as TypeReference).target;
}
// No further restrictions for static properties
if (flags & ModifierFlags.Static) {
@@ -28435,6 +28433,18 @@ namespace ts {
return true;
}
function getEnclosingClassFromThisParameter(node: Node): InterfaceType | undefined {
const thisParameter = getThisParameterFromNodeContext(node);
let thisType = thisParameter?.type && getTypeFromTypeNode(thisParameter.type);
if (thisType && thisType.flags & TypeFlags.TypeParameter) {
thisType = getConstraintOfTypeParameter(thisType as TypeParameter);
}
if (thisType && getObjectFlags(thisType) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference)) {
return getTargetType(thisType) as InterfaceType;
}
return undefined;
}
function getThisParameterFromNodeContext(node: Node) {
const thisContainer = getThisContainer(node, /* includeArrowFunctions */ false);
return thisContainer && isFunctionLike(thisContainer) ? getThisParameter(thisContainer) : undefined;