Skip to content

Commit 5f6ebf9

Browse files
authored
Show error when trying to instantiate a union of abstract and concrete constructors (microsoft#48114)
* check composite signatures for abstract flag * add tests and baselines * refactor someSignature into a single function
1 parent c1783b2 commit 5f6ebf9

6 files changed

+303
-1
lines changed

src/compiler/checker.ts

+8-1
Original file line numberDiff line numberDiff line change
@@ -30800,7 +30800,7 @@ namespace ts {
3080030800
// then it cannot be instantiated.
3080130801
// In the case of a merged class-module or class-interface declaration,
3080230802
// only the class declaration node will have the Abstract flag set.
30803-
if (constructSignatures.some(signature => signature.flags & SignatureFlags.Abstract)) {
30803+
if (someSignature(constructSignatures, signature => !!(signature.flags & SignatureFlags.Abstract))) {
3080430804
error(node, Diagnostics.Cannot_create_an_instance_of_an_abstract_class);
3080530805
return resolveErrorCall(node);
3080630806
}
@@ -30835,6 +30835,13 @@ namespace ts {
3083530835
return resolveErrorCall(node);
3083630836
}
3083730837

30838+
function someSignature(signatures: Signature | readonly Signature[], f: (s: Signature) => boolean): boolean {
30839+
if (isArray(signatures)) {
30840+
return some(signatures, signature => someSignature(signature, f));
30841+
}
30842+
return signatures.compositeKind === TypeFlags.Union ? some(signatures.compositeSignatures, f) : f(signatures);
30843+
}
30844+
3083830845
function typeHasProtectedAccessibleBase(target: Symbol, type: InterfaceType): boolean {
3083930846
const baseTypes = getBaseTypes(type);
3084030847
if (!length(baseTypes)) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
tests/cases/compiler/abstractClassUnionInstantiation.ts(14,1): error TS2511: Cannot create an instance of an abstract class.
2+
tests/cases/compiler/abstractClassUnionInstantiation.ts(15,1): error TS2511: Cannot create an instance of an abstract class.
3+
tests/cases/compiler/abstractClassUnionInstantiation.ts(18,46): error TS2511: Cannot create an instance of an abstract class.
4+
tests/cases/compiler/abstractClassUnionInstantiation.ts(19,46): error TS2511: Cannot create an instance of an abstract class.
5+
tests/cases/compiler/abstractClassUnionInstantiation.ts(21,35): error TS2511: Cannot create an instance of an abstract class.
6+
7+
8+
==== tests/cases/compiler/abstractClassUnionInstantiation.ts (5 errors) ====
9+
class ConcreteA {}
10+
class ConcreteB {}
11+
abstract class AbstractA { a: string; }
12+
abstract class AbstractB { b: string; }
13+
14+
type Abstracts = typeof AbstractA | typeof AbstractB;
15+
type Concretes = typeof ConcreteA | typeof ConcreteB;
16+
type ConcretesOrAbstracts = Concretes | Abstracts;
17+
18+
declare const cls1: ConcretesOrAbstracts;
19+
declare const cls2: Abstracts;
20+
declare const cls3: Concretes;
21+
22+
new cls1(); // should error
23+
~~~~~~~~~~
24+
!!! error TS2511: Cannot create an instance of an abstract class.
25+
new cls2(); // should error
26+
~~~~~~~~~~
27+
!!! error TS2511: Cannot create an instance of an abstract class.
28+
new cls3(); // should work
29+
30+
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
31+
~~~~~~~~~
32+
!!! error TS2511: Cannot create an instance of an abstract class.
33+
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
34+
~~~~~~~~~
35+
!!! error TS2511: Cannot create an instance of an abstract class.
36+
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
37+
[AbstractA, AbstractB].map(cls => new cls()); // should error
38+
~~~~~~~~~
39+
!!! error TS2511: Cannot create an instance of an abstract class.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//// [abstractClassUnionInstantiation.ts]
2+
class ConcreteA {}
3+
class ConcreteB {}
4+
abstract class AbstractA { a: string; }
5+
abstract class AbstractB { b: string; }
6+
7+
type Abstracts = typeof AbstractA | typeof AbstractB;
8+
type Concretes = typeof ConcreteA | typeof ConcreteB;
9+
type ConcretesOrAbstracts = Concretes | Abstracts;
10+
11+
declare const cls1: ConcretesOrAbstracts;
12+
declare const cls2: Abstracts;
13+
declare const cls3: Concretes;
14+
15+
new cls1(); // should error
16+
new cls2(); // should error
17+
new cls3(); // should work
18+
19+
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
20+
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
21+
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
22+
[AbstractA, AbstractB].map(cls => new cls()); // should error
23+
24+
//// [abstractClassUnionInstantiation.js]
25+
var ConcreteA = /** @class */ (function () {
26+
function ConcreteA() {
27+
}
28+
return ConcreteA;
29+
}());
30+
var ConcreteB = /** @class */ (function () {
31+
function ConcreteB() {
32+
}
33+
return ConcreteB;
34+
}());
35+
var AbstractA = /** @class */ (function () {
36+
function AbstractA() {
37+
}
38+
return AbstractA;
39+
}());
40+
var AbstractB = /** @class */ (function () {
41+
function AbstractB() {
42+
}
43+
return AbstractB;
44+
}());
45+
new cls1(); // should error
46+
new cls2(); // should error
47+
new cls3(); // should work
48+
[ConcreteA, AbstractA, AbstractB].map(function (cls) { return new cls(); }); // should error
49+
[AbstractA, AbstractB, ConcreteA].map(function (cls) { return new cls(); }); // should error
50+
[ConcreteA, ConcreteB].map(function (cls) { return new cls(); }); // should work
51+
[AbstractA, AbstractB].map(function (cls) { return new cls(); }); // should error
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
=== tests/cases/compiler/abstractClassUnionInstantiation.ts ===
2+
class ConcreteA {}
3+
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
4+
5+
class ConcreteB {}
6+
>ConcreteB : Symbol(ConcreteB, Decl(abstractClassUnionInstantiation.ts, 0, 18))
7+
8+
abstract class AbstractA { a: string; }
9+
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
10+
>a : Symbol(AbstractA.a, Decl(abstractClassUnionInstantiation.ts, 2, 26))
11+
12+
abstract class AbstractB { b: string; }
13+
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
14+
>b : Symbol(AbstractB.b, Decl(abstractClassUnionInstantiation.ts, 3, 26))
15+
16+
type Abstracts = typeof AbstractA | typeof AbstractB;
17+
>Abstracts : Symbol(Abstracts, Decl(abstractClassUnionInstantiation.ts, 3, 39))
18+
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
19+
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
20+
21+
type Concretes = typeof ConcreteA | typeof ConcreteB;
22+
>Concretes : Symbol(Concretes, Decl(abstractClassUnionInstantiation.ts, 5, 53))
23+
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
24+
>ConcreteB : Symbol(ConcreteB, Decl(abstractClassUnionInstantiation.ts, 0, 18))
25+
26+
type ConcretesOrAbstracts = Concretes | Abstracts;
27+
>ConcretesOrAbstracts : Symbol(ConcretesOrAbstracts, Decl(abstractClassUnionInstantiation.ts, 6, 53))
28+
>Concretes : Symbol(Concretes, Decl(abstractClassUnionInstantiation.ts, 5, 53))
29+
>Abstracts : Symbol(Abstracts, Decl(abstractClassUnionInstantiation.ts, 3, 39))
30+
31+
declare const cls1: ConcretesOrAbstracts;
32+
>cls1 : Symbol(cls1, Decl(abstractClassUnionInstantiation.ts, 9, 13))
33+
>ConcretesOrAbstracts : Symbol(ConcretesOrAbstracts, Decl(abstractClassUnionInstantiation.ts, 6, 53))
34+
35+
declare const cls2: Abstracts;
36+
>cls2 : Symbol(cls2, Decl(abstractClassUnionInstantiation.ts, 10, 13))
37+
>Abstracts : Symbol(Abstracts, Decl(abstractClassUnionInstantiation.ts, 3, 39))
38+
39+
declare const cls3: Concretes;
40+
>cls3 : Symbol(cls3, Decl(abstractClassUnionInstantiation.ts, 11, 13))
41+
>Concretes : Symbol(Concretes, Decl(abstractClassUnionInstantiation.ts, 5, 53))
42+
43+
new cls1(); // should error
44+
>cls1 : Symbol(cls1, Decl(abstractClassUnionInstantiation.ts, 9, 13))
45+
46+
new cls2(); // should error
47+
>cls2 : Symbol(cls2, Decl(abstractClassUnionInstantiation.ts, 10, 13))
48+
49+
new cls3(); // should work
50+
>cls3 : Symbol(cls3, Decl(abstractClassUnionInstantiation.ts, 11, 13))
51+
52+
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
53+
>[ConcreteA, AbstractA, AbstractB].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
54+
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
55+
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
56+
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
57+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
58+
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 17, 38))
59+
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 17, 38))
60+
61+
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
62+
>[AbstractA, AbstractB, ConcreteA].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
63+
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
64+
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
65+
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
66+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
67+
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 18, 38))
68+
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 18, 38))
69+
70+
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
71+
>[ConcreteA, ConcreteB].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
72+
>ConcreteA : Symbol(ConcreteA, Decl(abstractClassUnionInstantiation.ts, 0, 0))
73+
>ConcreteB : Symbol(ConcreteB, Decl(abstractClassUnionInstantiation.ts, 0, 18))
74+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
75+
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 19, 27))
76+
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 19, 27))
77+
78+
[AbstractA, AbstractB].map(cls => new cls()); // should error
79+
>[AbstractA, AbstractB].map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
80+
>AbstractA : Symbol(AbstractA, Decl(abstractClassUnionInstantiation.ts, 1, 18))
81+
>AbstractB : Symbol(AbstractB, Decl(abstractClassUnionInstantiation.ts, 2, 39))
82+
>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --))
83+
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 20, 27))
84+
>cls : Symbol(cls, Decl(abstractClassUnionInstantiation.ts, 20, 27))
85+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
=== tests/cases/compiler/abstractClassUnionInstantiation.ts ===
2+
class ConcreteA {}
3+
>ConcreteA : ConcreteA
4+
5+
class ConcreteB {}
6+
>ConcreteB : ConcreteB
7+
8+
abstract class AbstractA { a: string; }
9+
>AbstractA : AbstractA
10+
>a : string
11+
12+
abstract class AbstractB { b: string; }
13+
>AbstractB : AbstractB
14+
>b : string
15+
16+
type Abstracts = typeof AbstractA | typeof AbstractB;
17+
>Abstracts : Abstracts
18+
>AbstractA : typeof AbstractA
19+
>AbstractB : typeof AbstractB
20+
21+
type Concretes = typeof ConcreteA | typeof ConcreteB;
22+
>Concretes : Concretes
23+
>ConcreteA : typeof ConcreteA
24+
>ConcreteB : typeof ConcreteB
25+
26+
type ConcretesOrAbstracts = Concretes | Abstracts;
27+
>ConcretesOrAbstracts : ConcretesOrAbstracts
28+
29+
declare const cls1: ConcretesOrAbstracts;
30+
>cls1 : ConcretesOrAbstracts
31+
32+
declare const cls2: Abstracts;
33+
>cls2 : Abstracts
34+
35+
declare const cls3: Concretes;
36+
>cls3 : Concretes
37+
38+
new cls1(); // should error
39+
>new cls1() : any
40+
>cls1 : ConcretesOrAbstracts
41+
42+
new cls2(); // should error
43+
>new cls2() : any
44+
>cls2 : Abstracts
45+
46+
new cls3(); // should work
47+
>new cls3() : ConcreteA | ConcreteB
48+
>cls3 : Concretes
49+
50+
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
51+
>[ConcreteA, AbstractA, AbstractB].map(cls => new cls()) : any[]
52+
>[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[]
53+
>[ConcreteA, AbstractA, AbstractB] : (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]
54+
>ConcreteA : typeof ConcreteA
55+
>AbstractA : typeof AbstractA
56+
>AbstractB : typeof AbstractB
57+
>map : <U>(callbackfn: (value: typeof ConcreteA | typeof AbstractA | typeof AbstractB, index: number, array: (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
58+
>cls => new cls() : (cls: typeof ConcreteA | typeof AbstractA | typeof AbstractB) => any
59+
>cls : typeof ConcreteA | typeof AbstractA | typeof AbstractB
60+
>new cls() : any
61+
>cls : typeof ConcreteA | typeof AbstractA | typeof AbstractB
62+
63+
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
64+
>[AbstractA, AbstractB, ConcreteA].map(cls => new cls()) : any[]
65+
>[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[]
66+
>[AbstractA, AbstractB, ConcreteA] : (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]
67+
>AbstractA : typeof AbstractA
68+
>AbstractB : typeof AbstractB
69+
>ConcreteA : typeof ConcreteA
70+
>map : <U>(callbackfn: (value: typeof ConcreteA | typeof AbstractA | typeof AbstractB, index: number, array: (typeof ConcreteA | typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
71+
>cls => new cls() : (cls: typeof ConcreteA | typeof AbstractA | typeof AbstractB) => any
72+
>cls : typeof ConcreteA | typeof AbstractA | typeof AbstractB
73+
>new cls() : any
74+
>cls : typeof ConcreteA | typeof AbstractA | typeof AbstractB
75+
76+
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
77+
>[ConcreteA, ConcreteB].map(cls => new cls()) : ConcreteA[]
78+
>[ConcreteA, ConcreteB].map : <U>(callbackfn: (value: typeof ConcreteA, index: number, array: (typeof ConcreteA)[]) => U, thisArg?: any) => U[]
79+
>[ConcreteA, ConcreteB] : (typeof ConcreteA)[]
80+
>ConcreteA : typeof ConcreteA
81+
>ConcreteB : typeof ConcreteB
82+
>map : <U>(callbackfn: (value: typeof ConcreteA, index: number, array: (typeof ConcreteA)[]) => U, thisArg?: any) => U[]
83+
>cls => new cls() : (cls: typeof ConcreteA) => ConcreteA
84+
>cls : typeof ConcreteA
85+
>new cls() : ConcreteA
86+
>cls : typeof ConcreteA
87+
88+
[AbstractA, AbstractB].map(cls => new cls()); // should error
89+
>[AbstractA, AbstractB].map(cls => new cls()) : any[]
90+
>[AbstractA, AbstractB].map : <U>(callbackfn: (value: typeof AbstractA | typeof AbstractB, index: number, array: (typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
91+
>[AbstractA, AbstractB] : (typeof AbstractA | typeof AbstractB)[]
92+
>AbstractA : typeof AbstractA
93+
>AbstractB : typeof AbstractB
94+
>map : <U>(callbackfn: (value: typeof AbstractA | typeof AbstractB, index: number, array: (typeof AbstractA | typeof AbstractB)[]) => U, thisArg?: any) => U[]
95+
>cls => new cls() : (cls: typeof AbstractA | typeof AbstractB) => any
96+
>cls : typeof AbstractA | typeof AbstractB
97+
>new cls() : any
98+
>cls : typeof AbstractA | typeof AbstractB
99+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
class ConcreteA {}
2+
class ConcreteB {}
3+
abstract class AbstractA { a: string; }
4+
abstract class AbstractB { b: string; }
5+
6+
type Abstracts = typeof AbstractA | typeof AbstractB;
7+
type Concretes = typeof ConcreteA | typeof ConcreteB;
8+
type ConcretesOrAbstracts = Concretes | Abstracts;
9+
10+
declare const cls1: ConcretesOrAbstracts;
11+
declare const cls2: Abstracts;
12+
declare const cls3: Concretes;
13+
14+
new cls1(); // should error
15+
new cls2(); // should error
16+
new cls3(); // should work
17+
18+
[ConcreteA, AbstractA, AbstractB].map(cls => new cls()); // should error
19+
[AbstractA, AbstractB, ConcreteA].map(cls => new cls()); // should error
20+
[ConcreteA, ConcreteB].map(cls => new cls()); // should work
21+
[AbstractA, AbstractB].map(cls => new cls()); // should error

0 commit comments

Comments
 (0)