Show error when trying to instantiate a union of abstract and concrete constructors (#48114)

* check composite signatures for abstract flag

* add tests and baselines

* refactor someSignature into a single function
This commit is contained in:
jihndai
2022-03-04 17:39:05 -08:00
committed by GitHub
parent c1783b28e7
commit 5f6ebf9d78
6 changed files with 303 additions and 1 deletions
+8 -1
View File
@@ -30800,7 +30800,7 @@ namespace ts {
// then it cannot be instantiated.
// In the case of a merged class-module or class-interface declaration,
// only the class declaration node will have the Abstract flag set.
if (constructSignatures.some(signature => signature.flags & SignatureFlags.Abstract)) {
if (someSignature(constructSignatures, signature => !!(signature.flags & SignatureFlags.Abstract))) {
error(node, Diagnostics.Cannot_create_an_instance_of_an_abstract_class);
return resolveErrorCall(node);
}
@@ -30835,6 +30835,13 @@ namespace ts {
return resolveErrorCall(node);
}
function someSignature(signatures: Signature | readonly Signature[], f: (s: Signature) => boolean): boolean {
if (isArray(signatures)) {
return some(signatures, signature => someSignature(signature, f));
}
return signatures.compositeKind === TypeFlags.Union ? some(signatures.compositeSignatures, f) : f(signatures);
}
function typeHasProtectedAccessibleBase(target: Symbol, type: InterfaceType): boolean {
const baseTypes = getBaseTypes(type);
if (!length(baseTypes)) {
@@ -0,0 +1,39 @@
tests/cases/compiler/abstractClassUnionInstantiation.ts(14,1): error TS2511: Cannot create an instance of an abstract class.
tests/cases/compiler/abstractClassUnionInstantiation.ts(15,1): error TS2511: Cannot create an instance of an abstract class.
tests/cases/compiler/abstractClassUnionInstantiation.ts(18,46): error TS2511: Cannot create an instance of an abstract class.
tests/cases/compiler/abstractClassUnionInstantiation.ts(19,46): error TS2511: Cannot create an instance of an abstract class.
tests/cases/compiler/abstractClassUnionInstantiation.ts(21,35): error TS2511: Cannot create an instance of an abstract class.
==== tests/cases/compiler/abstractClassUnionInstantiation.ts (5 errors) ====
class ConcreteA {}
class ConcreteB {}
abstract class AbstractA { a: string; }
abstract class AbstractB { b: string; }
type Abstracts = typeof AbstractA | typeof AbstractB;
type Concretes = typeof ConcreteA | typeof ConcreteB;
type ConcretesOrAbstracts = Concretes | Abstracts;
declare const cls1: ConcretesOrAbstracts;
declare const cls2: Abstracts;
declare const cls3: Concretes;
new cls1(); // should error
~~~~~~~~~~
!!! error TS2511: Cannot create an instance of an abstract class.
new cls2(); // should error
~~~~~~~~~~
!!! error TS2511: Cannot create an instance of an abstract class.
new cls3(); // should work
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
~~~~~~~~~
!!! error TS2511: Cannot create an instance of an abstract class.
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
~~~~~~~~~
!!! error TS2511: Cannot create an instance of an abstract class.
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
[AbstractA, AbstractB].map(cls => new cls()); // should error
~~~~~~~~~
!!! error TS2511: Cannot create an instance of an abstract class.
@@ -0,0 +1,51 @@
//// [abstractClassUnionInstantiation.ts]
class ConcreteA {}
class ConcreteB {}
abstract class AbstractA { a: string; }
abstract class AbstractB { b: string; }
type Abstracts = typeof AbstractA | typeof AbstractB;
type Concretes = typeof ConcreteA | typeof ConcreteB;
type ConcretesOrAbstracts = Concretes | Abstracts;
declare const cls1: ConcretesOrAbstracts;
declare const cls2: Abstracts;
declare const cls3: Concretes;
new cls1(); // should error
new cls2(); // should error
new cls3(); // should work
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
[AbstractA, AbstractB].map(cls => new cls()); // should error
//// [abstractClassUnionInstantiation.js]
var ConcreteA = /** @class */ (function () {
function ConcreteA() {
}
return ConcreteA;
}());
var ConcreteB = /** @class */ (function () {
function ConcreteB() {
}
return ConcreteB;
}());
var AbstractA = /** @class */ (function () {
function AbstractA() {
}
return AbstractA;
}());
var AbstractB = /** @class */ (function () {
function AbstractB() {
}
return AbstractB;
}());
new cls1(); // should error
new cls2(); // should error
new cls3(); // should work
[ConcreteA, AbstractA, AbstractB].map(function (cls) { return new cls(); }); // should error
[AbstractA, AbstractB, ConcreteA].map(function (cls) { return new cls(); }); // should error
[ConcreteA, ConcreteB].map(function (cls) { return new cls(); }); // should work
[AbstractA, AbstractB].map(function (cls) { return new cls(); }); // should error
@@ -0,0 +1,85 @@
=== tests/cases/compiler/abstractClassUnionInstantiation.ts ===
class ConcreteA {}
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
class ConcreteB {}
>ConcreteB : Symbol(ConcreteB, Decl(abstractClassUnionInstantiation.ts, 0, 18))
abstract class AbstractA { a: string; }
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
>a : Symbol(AbstractA.a, Decl(abstractClassUnionInstantiation.ts, 2, 26))
abstract class AbstractB { b: string; }
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
>b : Symbol(AbstractB.b, Decl(abstractClassUnionInstantiation.ts, 3, 26))
type Abstracts = typeof AbstractA | typeof AbstractB;
>Abstracts : Symbol(Abstracts, Decl(abstractClassUnionInstantiation.ts, 3, 39))
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
type Concretes = typeof ConcreteA | typeof ConcreteB;
>Concretes : Symbol(Concretes, Decl(abstractClassUnionInstantiation.ts, 5, 53))
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
>ConcreteB : Symbol(ConcreteB, Decl(abstractClassUnionInstantiation.ts, 0, 18))
type ConcretesOrAbstracts = Concretes | Abstracts;
>ConcretesOrAbstracts : Symbol(ConcretesOrAbstracts, Decl(abstractClassUnionInstantiation.ts, 6, 53))
>Concretes : Symbol(Concretes, Decl(abstractClassUnionInstantiation.ts, 5, 53))
>Abstracts : Symbol(Abstracts, Decl(abstractClassUnionInstantiation.ts, 3, 39))
declare const cls1: ConcretesOrAbstracts;
>cls1 : Symbol(cls1, Decl(abstractClassUnionInstantiation.ts, 9, 13))
>ConcretesOrAbstracts : Symbol(ConcretesOrAbstracts, Decl(abstractClassUnionInstantiation.ts, 6, 53))
declare const cls2: Abstracts;
>cls2 : Symbol(cls2, Decl(abstractClassUnionInstantiation.ts, 10, 13))
>Abstracts : Symbol(Abstracts, Decl(abstractClassUnionInstantiation.ts, 3, 39))
declare const cls3: Concretes;
>cls3 : Symbol(cls3, Decl(abstractClassUnionInstantiation.ts, 11, 13))
>Concretes : Symbol(Concretes, Decl(abstractClassUnionInstantiation.ts, 5, 53))
new cls1(); // should error
>cls1 : Symbol(cls1, Decl(abstractClassUnionInstantiation.ts, 9, 13))
new cls2(); // should error
>cls2 : Symbol(cls2, Decl(abstractClassUnionInstantiation.ts, 10, 13))
new cls3(); // should work
>cls3 : Symbol(cls3, Decl(abstractClassUnionInstantiation.ts, 11, 13))
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
>[ConcreteA, AbstractA, AbstractB].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 17, 38))
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 17, 38))
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
>[AbstractA, AbstractB, ConcreteA].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 18, 38))
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 18, 38))
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
>[ConcreteA, ConcreteB].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
>ConcreteB : Symbol(ConcreteB, Decl(abstractClassUnionInstantiation.ts, 0, 18))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 19, 27))
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 19, 27))
[AbstractA, AbstractB].map(cls => new cls()); // should error
>[AbstractA, AbstractB].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 20, 27))
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 20, 27))
@@ -0,0 +1,99 @@
=== tests/cases/compiler/abstractClassUnionInstantiation.ts ===
class ConcreteA {}
>ConcreteA : ConcreteA
class ConcreteB {}
>ConcreteB : ConcreteB
abstract class AbstractA { a: string; }
>AbstractA : AbstractA
>a : string
abstract class AbstractB { b: string; }
>AbstractB : AbstractB
>b : string
type Abstracts = typeof AbstractA | typeof AbstractB;
>Abstracts : Abstracts
>AbstractA : typeof AbstractA
>AbstractB : typeof AbstractB
type Concretes = typeof ConcreteA | typeof ConcreteB;
>Concretes : Concretes
>ConcreteA : typeof ConcreteA
>ConcreteB : typeof ConcreteB
type ConcretesOrAbstracts = Concretes | Abstracts;
>ConcretesOrAbstracts : ConcretesOrAbstracts
declare const cls1: ConcretesOrAbstracts;
>cls1 : ConcretesOrAbstracts
declare const cls2: Abstracts;
>cls2 : Abstracts
declare const cls3: Concretes;
>cls3 : Concretes
new cls1(); // should error
>new cls1() : any
>cls1 : ConcretesOrAbstracts
new cls2(); // should error
>new cls2() : any
>cls2 : Abstracts
new cls3(); // should work
>new cls3() : ConcreteA | ConcreteB
>cls3 : Concretes
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
>[ConcreteA, AbstractA, AbstractB].map(cls => new cls()) : any[]
>[ConcreteA, AbstractA, AbstractB].map : <U>(callbackfn: (value: typeof ConcreteA | typeof AbstractA | typeof AbstractB, index: number, array: (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
>[ConcreteA, AbstractA, AbstractB] : (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]
>ConcreteA : typeof ConcreteA
>AbstractA : typeof AbstractA
>AbstractB : typeof AbstractB
>map : <U>(callbackfn: (value: typeof ConcreteA | typeof AbstractA | typeof AbstractB, index: number, array: (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
>cls => new cls() : (cls: typeof ConcreteA | typeof AbstractA | typeof AbstractB) => any
>cls : typeof ConcreteA | typeof AbstractA | typeof AbstractB
>new cls() : any
>cls : typeof ConcreteA | typeof AbstractA | typeof AbstractB
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
>[AbstractA, AbstractB, ConcreteA].map(cls => new cls()) : any[]
>[AbstractA, AbstractB, ConcreteA].map : <U>(callbackfn: (value: typeof ConcreteA | typeof AbstractA | typeof AbstractB, index: number, array: (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
>[AbstractA, AbstractB, ConcreteA] : (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]
>AbstractA : typeof AbstractA
>AbstractB : typeof AbstractB
>ConcreteA : typeof ConcreteA
>map : <U>(callbackfn: (value: typeof ConcreteA | typeof AbstractA | typeof AbstractB, index: number, array: (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
>cls => new cls() : (cls: typeof ConcreteA | typeof AbstractA | typeof AbstractB) => any
>cls : typeof ConcreteA | typeof AbstractA | typeof AbstractB
>new cls() : any
>cls : typeof ConcreteA | typeof AbstractA | typeof AbstractB
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
>[ConcreteA, ConcreteB].map(cls => new cls()) : ConcreteA[]
>[ConcreteA, ConcreteB].map : <U>(callbackfn: (value: typeof ConcreteA, index: number, array: (typeof ConcreteA)[]) => U, thisArg?: any) => U[]
>[ConcreteA, ConcreteB] : (typeof ConcreteA)[]
>ConcreteA : typeof ConcreteA
>ConcreteB : typeof ConcreteB
>map : <U>(callbackfn: (value: typeof ConcreteA, index: number, array: (typeof ConcreteA)[]) => U, thisArg?: any) => U[]
>cls => new cls() : (cls: typeof ConcreteA) => ConcreteA
>cls : typeof ConcreteA
>new cls() : ConcreteA
>cls : typeof ConcreteA
[AbstractA, AbstractB].map(cls => new cls()); // should error
>[AbstractA, AbstractB].map(cls => new cls()) : any[]
>[AbstractA, AbstractB].map : <U>(callbackfn: (value: typeof AbstractA | typeof AbstractB, index: number, array: (typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
>[AbstractA, AbstractB] : (typeof AbstractA | typeof AbstractB)[]
>AbstractA : typeof AbstractA
>AbstractB : typeof AbstractB
>map : <U>(callbackfn: (value: typeof AbstractA | typeof AbstractB, index: number, array: (typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
>cls => new cls() : (cls: typeof AbstractA | typeof AbstractB) => any
>cls : typeof AbstractA | typeof AbstractB
>new cls() : any
>cls : typeof AbstractA | typeof AbstractB
@@ -0,0 +1,21 @@
class ConcreteA {}
class ConcreteB {}
abstract class AbstractA { a: string; }
abstract class AbstractB { b: string; }
type Abstracts = typeof AbstractA | typeof AbstractB;
type Concretes = typeof ConcreteA | typeof ConcreteB;
type ConcretesOrAbstracts = Concretes | Abstracts;
declare const cls1: ConcretesOrAbstracts;
declare const cls2: Abstracts;
declare const cls3: Concretes;
new cls1(); // should error
new cls2(); // should error
new cls3(); // should work
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
[AbstractA, AbstractB].map(cls => new cls()); // should error