mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Handle intersection types when looking up base types for visibility (#25418)
* Handle intersection types when looking up base types for visibility * Extract protected constructor check to function and recur on intersections * Remove unneeded cast
This commit is contained in:
+35
-9
@@ -19345,6 +19345,38 @@ namespace ts {
|
||||
return resolveErrorCall(node);
|
||||
}
|
||||
|
||||
function typeHasProtectedAccessibleBase(target: Symbol, type: InterfaceType): boolean {
|
||||
const baseTypes = getBaseTypes(type);
|
||||
if (!length(baseTypes)) {
|
||||
return false;
|
||||
}
|
||||
const firstBase = baseTypes[0];
|
||||
if (firstBase.flags & TypeFlags.Intersection) {
|
||||
const types = (firstBase as IntersectionType).types;
|
||||
const mixinCount = countWhere(types, isMixinConstructorType);
|
||||
let i = 0;
|
||||
for (const intersectionMember of (firstBase as IntersectionType).types) {
|
||||
i++;
|
||||
// We want to ignore mixin ctors
|
||||
if (mixinCount === 0 || mixinCount === types.length && i === 0 || !isMixinConstructorType(intersectionMember)) {
|
||||
if (getObjectFlags(intersectionMember) & (ObjectFlags.Class | ObjectFlags.Interface)) {
|
||||
if (intersectionMember.symbol === target) {
|
||||
return true;
|
||||
}
|
||||
if (typeHasProtectedAccessibleBase(target, intersectionMember as InterfaceType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (firstBase.symbol === target) {
|
||||
return true;
|
||||
}
|
||||
return typeHasProtectedAccessibleBase(target, firstBase as InterfaceType);
|
||||
}
|
||||
|
||||
function isConstructorAccessible(node: NewExpression, signature: Signature) {
|
||||
if (!signature || !signature.declaration) {
|
||||
return true;
|
||||
@@ -19364,16 +19396,10 @@ namespace ts {
|
||||
// A private or protected constructor can only be instantiated within its own class (or a subclass, for protected)
|
||||
if (!isNodeWithinClass(node, declaringClassDeclaration)) {
|
||||
const containingClass = getContainingClass(node);
|
||||
if (containingClass) {
|
||||
if (containingClass && modifiers & ModifierFlags.Protected) {
|
||||
const containingType = getTypeOfNode(containingClass);
|
||||
let baseTypes = getBaseTypes(containingType as InterfaceType);
|
||||
while (baseTypes.length) {
|
||||
const baseType = baseTypes[0];
|
||||
if (modifiers & ModifierFlags.Protected &&
|
||||
baseType.symbol === declaration.parent.symbol) {
|
||||
return true;
|
||||
}
|
||||
baseTypes = getBaseTypes(baseType as InterfaceType);
|
||||
if (typeHasProtectedAccessibleBase(declaration.parent.symbol, containingType as InterfaceType)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (modifiers & ModifierFlags.Private) {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
tests/cases/compiler/noCrashOnMixin.ts(21,9): error TS2674: Constructor of class 'Abstract' is protected and only accessible within the class declaration.
|
||||
|
||||
|
||||
==== tests/cases/compiler/noCrashOnMixin.ts (1 errors) ====
|
||||
class Abstract {
|
||||
protected constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
class Concrete extends Abstract {
|
||||
}
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
function Mixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
};
|
||||
}
|
||||
|
||||
class Empty {
|
||||
}
|
||||
|
||||
class CrashTrigger extends Mixin(Empty) {
|
||||
public trigger() {
|
||||
new Concrete();
|
||||
~~~~~~~~~~~~~~
|
||||
!!! error TS2674: Constructor of class 'Abstract' is protected and only accessible within the class declaration.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
//// [noCrashOnMixin.ts]
|
||||
class Abstract {
|
||||
protected constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
class Concrete extends Abstract {
|
||||
}
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
function Mixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
};
|
||||
}
|
||||
|
||||
class Empty {
|
||||
}
|
||||
|
||||
class CrashTrigger extends Mixin(Empty) {
|
||||
public trigger() {
|
||||
new Concrete();
|
||||
}
|
||||
}
|
||||
|
||||
//// [noCrashOnMixin.js]
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
||||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
|
||||
return extendStatics(d, b);
|
||||
}
|
||||
return function (d, b) {
|
||||
extendStatics(d, b);
|
||||
function __() { this.constructor = d; }
|
||||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
||||
};
|
||||
})();
|
||||
var Abstract = /** @class */ (function () {
|
||||
function Abstract() {
|
||||
}
|
||||
return Abstract;
|
||||
}());
|
||||
var Concrete = /** @class */ (function (_super) {
|
||||
__extends(Concrete, _super);
|
||||
function Concrete() {
|
||||
return _super !== null && _super.apply(this, arguments) || this;
|
||||
}
|
||||
return Concrete;
|
||||
}(Abstract));
|
||||
function Mixin(Base) {
|
||||
return /** @class */ (function (_super) {
|
||||
__extends(class_1, _super);
|
||||
function class_1() {
|
||||
return _super !== null && _super.apply(this, arguments) || this;
|
||||
}
|
||||
return class_1;
|
||||
}(Base));
|
||||
}
|
||||
var Empty = /** @class */ (function () {
|
||||
function Empty() {
|
||||
}
|
||||
return Empty;
|
||||
}());
|
||||
var CrashTrigger = /** @class */ (function (_super) {
|
||||
__extends(CrashTrigger, _super);
|
||||
function CrashTrigger() {
|
||||
return _super !== null && _super.apply(this, arguments) || this;
|
||||
}
|
||||
CrashTrigger.prototype.trigger = function () {
|
||||
new Concrete();
|
||||
};
|
||||
return CrashTrigger;
|
||||
}(Mixin(Empty)));
|
||||
@@ -0,0 +1,48 @@
|
||||
=== tests/cases/compiler/noCrashOnMixin.ts ===
|
||||
class Abstract {
|
||||
>Abstract : Symbol(Abstract, Decl(noCrashOnMixin.ts, 0, 0))
|
||||
|
||||
protected constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
class Concrete extends Abstract {
|
||||
>Concrete : Symbol(Concrete, Decl(noCrashOnMixin.ts, 3, 1))
|
||||
>Abstract : Symbol(Abstract, Decl(noCrashOnMixin.ts, 0, 0))
|
||||
}
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
>Constructor : Symbol(Constructor, Decl(noCrashOnMixin.ts, 6, 1))
|
||||
>T : Symbol(T, Decl(noCrashOnMixin.ts, 8, 17))
|
||||
>args : Symbol(args, Decl(noCrashOnMixin.ts, 8, 32))
|
||||
>T : Symbol(T, Decl(noCrashOnMixin.ts, 8, 17))
|
||||
|
||||
function Mixin<TBase extends Constructor>(Base: TBase) {
|
||||
>Mixin : Symbol(Mixin, Decl(noCrashOnMixin.ts, 8, 53))
|
||||
>TBase : Symbol(TBase, Decl(noCrashOnMixin.ts, 10, 15))
|
||||
>Constructor : Symbol(Constructor, Decl(noCrashOnMixin.ts, 6, 1))
|
||||
>Base : Symbol(Base, Decl(noCrashOnMixin.ts, 10, 42))
|
||||
>TBase : Symbol(TBase, Decl(noCrashOnMixin.ts, 10, 15))
|
||||
|
||||
return class extends Base {
|
||||
>Base : Symbol(Base, Decl(noCrashOnMixin.ts, 10, 42))
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
class Empty {
|
||||
>Empty : Symbol(Empty, Decl(noCrashOnMixin.ts, 13, 1))
|
||||
}
|
||||
|
||||
class CrashTrigger extends Mixin(Empty) {
|
||||
>CrashTrigger : Symbol(CrashTrigger, Decl(noCrashOnMixin.ts, 16, 1))
|
||||
>Mixin : Symbol(Mixin, Decl(noCrashOnMixin.ts, 8, 53))
|
||||
>Empty : Symbol(Empty, Decl(noCrashOnMixin.ts, 13, 1))
|
||||
|
||||
public trigger() {
|
||||
>trigger : Symbol(CrashTrigger.trigger, Decl(noCrashOnMixin.ts, 18, 41))
|
||||
|
||||
new Concrete();
|
||||
>Concrete : Symbol(Concrete, Decl(noCrashOnMixin.ts, 3, 1))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
=== tests/cases/compiler/noCrashOnMixin.ts ===
|
||||
class Abstract {
|
||||
>Abstract : Abstract
|
||||
|
||||
protected constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
class Concrete extends Abstract {
|
||||
>Concrete : Concrete
|
||||
>Abstract : Abstract
|
||||
}
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
>Constructor : Constructor<T>
|
||||
>T : T
|
||||
>args : any[]
|
||||
>T : T
|
||||
|
||||
function Mixin<TBase extends Constructor>(Base: TBase) {
|
||||
>Mixin : <TBase extends Constructor<{}>>(Base: TBase) => { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
|
||||
>TBase : TBase
|
||||
>Constructor : Constructor<T>
|
||||
>Base : TBase
|
||||
>TBase : TBase
|
||||
|
||||
return class extends Base {
|
||||
>class extends Base { } : { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
|
||||
>Base : {}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
class Empty {
|
||||
>Empty : Empty
|
||||
}
|
||||
|
||||
class CrashTrigger extends Mixin(Empty) {
|
||||
>CrashTrigger : CrashTrigger
|
||||
>Mixin(Empty) : Mixin<typeof Empty>.(Anonymous class) & Empty
|
||||
>Mixin : <TBase extends Constructor<{}>>(Base: TBase) => { new (...args: any[]): (Anonymous class); prototype: Mixin<any>.(Anonymous class); } & TBase
|
||||
>Empty : typeof Empty
|
||||
|
||||
public trigger() {
|
||||
>trigger : () => void
|
||||
|
||||
new Concrete();
|
||||
>new Concrete() : any
|
||||
>Concrete : typeof Concrete
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
class Abstract {
|
||||
protected constructor() {
|
||||
}
|
||||
}
|
||||
|
||||
class Concrete extends Abstract {
|
||||
}
|
||||
|
||||
type Constructor<T = {}> = new (...args: any[]) => T;
|
||||
|
||||
function Mixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
};
|
||||
}
|
||||
|
||||
class Empty {
|
||||
}
|
||||
|
||||
class CrashTrigger extends Mixin(Empty) {
|
||||
public trigger() {
|
||||
new Concrete();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user