diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5827a9b4e5f..eee32406b77 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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)) { diff --git a/tests/baselines/reference/abstractClassUnionInstantiation.errors.txt b/tests/baselines/reference/abstractClassUnionInstantiation.errors.txt new file mode 100644 index 00000000000..ff1eb399802 --- /dev/null +++ b/tests/baselines/reference/abstractClassUnionInstantiation.errors.txt @@ -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. \ No newline at end of file diff --git a/tests/baselines/reference/abstractClassUnionInstantiation.js b/tests/baselines/reference/abstractClassUnionInstantiation.js new file mode 100644 index 00000000000..c42d945625c --- /dev/null +++ b/tests/baselines/reference/abstractClassUnionInstantiation.js @@ -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 diff --git a/tests/baselines/reference/abstractClassUnionInstantiation.symbols b/tests/baselines/reference/abstractClassUnionInstantiation.symbols new file mode 100644 index 00000000000..453ce182441 --- /dev/null +++ b/tests/baselines/reference/abstractClassUnionInstantiation.symbols @@ -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)) + diff --git a/tests/baselines/reference/abstractClassUnionInstantiation.types b/tests/baselines/reference/abstractClassUnionInstantiation.types new file mode 100644 index 00000000000..969f0b85c44 --- /dev/null +++ b/tests/baselines/reference/abstractClassUnionInstantiation.types @@ -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 : (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 : (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 : (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 : (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 : (callbackfn: (value: typeof ConcreteA, index: number, array: (typeof ConcreteA)[]) => U, thisArg?: any) => U[] +>[ConcreteA, ConcreteB] : (typeof ConcreteA)[] +>ConcreteA : typeof ConcreteA +>ConcreteB : typeof ConcreteB +>map : (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 : (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 : (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 + diff --git a/tests/cases/compiler/abstractClassUnionInstantiation.ts b/tests/cases/compiler/abstractClassUnionInstantiation.ts new file mode 100644 index 00000000000..0b3b2e1feb2 --- /dev/null +++ b/tests/cases/compiler/abstractClassUnionInstantiation.ts @@ -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 \ No newline at end of file