diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 16fb7b31797ae..3983ba074e7e2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13965,8 +13965,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return length(target.typeParameters) === length(typeArguments) ? createTypeReference(target, concatenate(typeArguments, [thisArgument || target.thisType!])) : type; } else if (type.flags & TypeFlags.Intersection) { - const types = sameMap((type as IntersectionType).types, t => getTypeWithThisArgument(t, thisArgument, needApparentType)); - return types !== (type as IntersectionType).types ? getIntersectionType(types) : type; + const intersectionType = type as IntersectionType; + const types = sameMap(intersectionType.types, t => getTypeWithThisArgument(t, thisArgument, needApparentType)); + if (types === intersectionType.types) { + return type; + } + const result = getIntersectionType(types); + if (result.flags & TypeFlags.Intersection) { + (result as IntersectionType).withThisArgumentTarget = intersectionType; + } + return result; } return needApparentType ? getApparentType(type) : type; } @@ -15532,7 +15540,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { checkFlags |= (!(modifiers & ModifierFlags.NonPublicAccessibilityModifier) ? CheckFlags.ContainsPublic : 0) | (modifiers & ModifierFlags.Protected ? CheckFlags.ContainsProtected : 0) | (modifiers & ModifierFlags.Private ? CheckFlags.ContainsPrivate : 0) | - (modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0); + (modifiers & ModifierFlags.Static ? CheckFlags.ContainsStatic : 0) | + (modifiers & ModifierFlags.Abstract ? CheckFlags.ContainsAbstract : 0); if (!isPrototypeProperty(prop)) { syntheticFlag = CheckFlags.SyntheticProperty; } @@ -15645,6 +15654,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { result.links.writeType = isUnion ? getUnionType(writeTypes) : getIntersectionType(writeTypes); } } + if (!isUnion && (containingType as IntersectionType).withThisArgumentTarget) { + result.links.withThisArgumentIntersectionPropTarget = getUnionOrIntersectionProperty((containingType as IntersectionType).withThisArgumentTarget!, name, skipObjectFunctionPropertyAugment); + } return result; } @@ -47285,11 +47297,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { ); } - function getTargetSymbol(s: Symbol) { + function getTargetSymbol(s: Symbol): Symbol { + // NOTE: cast to TransientSymbol should be safe because only TransientSymbols have `CheckFlags.Instantiated | CheckFlags.Synthetic` + const checkFlags = getCheckFlags(s); + // if symbol is instantiated its flags are not copied from the 'target' // so we'll need to get back original 'target' symbol to work with correct set of flags - // NOTE: cast to TransientSymbol should be safe because only TransientSymbols have CheckFlags.Instantiated - return getCheckFlags(s) & CheckFlags.Instantiated ? (s as TransientSymbol).links.target! : s; + if (checkFlags & CheckFlags.Instantiated) { + return (s as TransientSymbol).links.target!; + } + if (checkFlags & CheckFlags.Synthetic && (s as TransientSymbol).links.withThisArgumentIntersectionPropTarget) { + return (s as TransientSymbol).links.withThisArgumentIntersectionPropTarget!; + } + return s; } function getClassOrInterfaceDeclarationsOfSymbol(symbol: Symbol) { diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 1cfe3e04ba68d..3dd3eb3ec7314 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6107,6 +6107,7 @@ export interface SymbolLinks { accessibleChainCache?: Map; filteredIndexSymbolCache?: Map //Symbol with applicable declarations requestedExternalEmitHelpers?: ExternalEmitHelpers; // External emit helpers already checked for this symbol. + withThisArgumentIntersectionPropTarget?: Symbol; } // dprint-ignore @@ -6125,15 +6126,16 @@ export const enum CheckFlags { ContainsProtected = 1 << 9, // Synthetic property with protected constituent(s) ContainsPrivate = 1 << 10, // Synthetic property with private constituent(s) ContainsStatic = 1 << 11, // Synthetic property with static constituent(s) - Late = 1 << 12, // Late-bound symbol for a computed property with a dynamic name - ReverseMapped = 1 << 13, // Property of reverse-inferred homomorphic mapped type - OptionalParameter = 1 << 14, // Optional parameter - RestParameter = 1 << 15, // Rest parameter - DeferredType = 1 << 16, // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType` - HasNeverType = 1 << 17, // Synthetic property with at least one never type in constituents - Mapped = 1 << 18, // Property of mapped type - StripOptional = 1 << 19, // Strip optionality in mapped property - Unresolved = 1 << 20, // Unresolved type alias symbol + ContainsAbstract = 1 << 12, // Synthetic property with abstract constituent(s) + Late = 1 << 13, // Late-bound symbol for a computed property with a dynamic name + ReverseMapped = 1 << 14, // Property of reverse-inferred homomorphic mapped type + OptionalParameter = 1 << 15, // Optional parameter + RestParameter = 1 << 16, // Rest parameter + DeferredType = 1 << 17, // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType` + HasNeverType = 1 << 18, // Synthetic property with at least one never type in constituents + Mapped = 1 << 19, // Property of mapped type + StripOptional = 1 << 20, // Strip optionality in mapped property + Unresolved = 1 << 21, // Unresolved type alias symbol Synthetic = SyntheticProperty | SyntheticMethod, Discriminant = HasNonUniformType | HasLiteralType, Partial = ReadPartial | WritePartial, @@ -6758,6 +6760,8 @@ export interface IntersectionType extends UnionOrIntersectionType { resolvedApparentType: Type; /** @internal */ uniqueLiteralFilledInstantiation?: Type; // Instantiation with type parameters mapped to never type + /** @internal */ + withThisArgumentTarget?: IntersectionType; } export type StructuredType = ObjectType | UnionType | IntersectionType; diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 4687bb8796fad..629674ae73fd8 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -7978,14 +7978,14 @@ export function getDeclarationModifierFlagsFromSymbol(s: Symbol, isWrite = false const flags = getCombinedModifierFlags(declaration); return s.parent && s.parent.flags & SymbolFlags.Class ? flags : flags & ~ModifierFlags.AccessibilityModifier; } - if (getCheckFlags(s) & CheckFlags.Synthetic) { - // NOTE: potentially unchecked cast to TransientSymbol - const checkFlags = (s as TransientSymbol).links.checkFlags; + const checkFlags = getCheckFlags(s); + if (checkFlags & CheckFlags.Synthetic) { const accessModifier = checkFlags & CheckFlags.ContainsPrivate ? ModifierFlags.Private : checkFlags & CheckFlags.ContainsPublic ? ModifierFlags.Public : ModifierFlags.Protected; const staticModifier = checkFlags & CheckFlags.ContainsStatic ? ModifierFlags.Static : 0; - return accessModifier | staticModifier; + const abstractModifier = checkFlags & CheckFlags.ContainsAbstract ? ModifierFlags.Abstract : 0; + return accessModifier | staticModifier | abstractModifier; } if (s.flags & SymbolFlags.Prototype) { return ModifierFlags.Public | ModifierFlags.Static; diff --git a/tests/baselines/reference/abstractIntersectedClasses1.errors.txt b/tests/baselines/reference/abstractIntersectedClasses1.errors.txt new file mode 100644 index 0000000000000..0ef81a4659e19 --- /dev/null +++ b/tests/baselines/reference/abstractIntersectedClasses1.errors.txt @@ -0,0 +1,52 @@ +abstractIntersectedClasses1.ts(17,7): error TS2654: Non-abstract class 'Foo1' is missing implementations for the following members of 'A & B': 'a', 'b'. +abstractIntersectedClasses1.ts(18,7): error TS2515: Non-abstract class 'Foo2' does not implement inherited abstract member b from class 'A & B'. +abstractIntersectedClasses1.ts(34,7): error TS2515: Non-abstract class 'Bar1' does not implement inherited abstract member a from class 'A & A2'. + + +==== abstractIntersectedClasses1.ts (3 errors) ==== + // https://github.com/microsoft/TypeScript/issues/56738 + + abstract class A { + abstract a(): number; + } + + abstract class A2 { + abstract a(): number; + } + + abstract class B { + abstract b(): number; + } + + declare const Base: abstract new () => A & B; + + class Foo1 extends Base {} // error + ~~~~ +!!! error TS2654: Non-abstract class 'Foo1' is missing implementations for the following members of 'A & B': 'a', 'b'. + class Foo2 extends Base { // error + ~~~~ +!!! error TS2515: Non-abstract class 'Foo2' does not implement inherited abstract member b from class 'A & B'. + a() { + return 10; + } + } + class Foo3 extends Base { // ok + a() { + return 10; + } + b() { + return 42; + } + } + + declare const Base2: abstract new () => A & A2; + + class Bar1 extends Base2 {} // error + ~~~~ +!!! error TS2515: Non-abstract class 'Bar1' does not implement inherited abstract member a from class 'A & A2'. + class Bar2 extends Base2 { // ok + a() { + return 100; + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/abstractIntersectedClasses1.symbols b/tests/baselines/reference/abstractIntersectedClasses1.symbols new file mode 100644 index 0000000000000..7ecfce4836458 --- /dev/null +++ b/tests/baselines/reference/abstractIntersectedClasses1.symbols @@ -0,0 +1,81 @@ +//// [tests/cases/compiler/abstractIntersectedClasses1.ts] //// + +=== abstractIntersectedClasses1.ts === +// https://github.com/microsoft/TypeScript/issues/56738 + +abstract class A { +>A : Symbol(A, Decl(abstractIntersectedClasses1.ts, 0, 0)) + + abstract a(): number; +>a : Symbol(A.a, Decl(abstractIntersectedClasses1.ts, 2, 18)) +} + +abstract class A2 { +>A2 : Symbol(A2, Decl(abstractIntersectedClasses1.ts, 4, 1)) + + abstract a(): number; +>a : Symbol(A2.a, Decl(abstractIntersectedClasses1.ts, 6, 19)) +} + +abstract class B { +>B : Symbol(B, Decl(abstractIntersectedClasses1.ts, 8, 1)) + + abstract b(): number; +>b : Symbol(B.b, Decl(abstractIntersectedClasses1.ts, 10, 18)) +} + +declare const Base: abstract new () => A & B; +>Base : Symbol(Base, Decl(abstractIntersectedClasses1.ts, 14, 13)) +>A : Symbol(A, Decl(abstractIntersectedClasses1.ts, 0, 0)) +>B : Symbol(B, Decl(abstractIntersectedClasses1.ts, 8, 1)) + +class Foo1 extends Base {} // error +>Foo1 : Symbol(Foo1, Decl(abstractIntersectedClasses1.ts, 14, 45)) +>Base : Symbol(Base, Decl(abstractIntersectedClasses1.ts, 14, 13)) + +class Foo2 extends Base { // error +>Foo2 : Symbol(Foo2, Decl(abstractIntersectedClasses1.ts, 16, 26)) +>Base : Symbol(Base, Decl(abstractIntersectedClasses1.ts, 14, 13)) + + a() { +>a : Symbol(Foo2.a, Decl(abstractIntersectedClasses1.ts, 17, 25)) + + return 10; + } +} +class Foo3 extends Base { // ok +>Foo3 : Symbol(Foo3, Decl(abstractIntersectedClasses1.ts, 21, 1)) +>Base : Symbol(Base, Decl(abstractIntersectedClasses1.ts, 14, 13)) + + a() { +>a : Symbol(Foo3.a, Decl(abstractIntersectedClasses1.ts, 22, 25)) + + return 10; + } + b() { +>b : Symbol(Foo3.b, Decl(abstractIntersectedClasses1.ts, 25, 3)) + + return 42; + } +} + +declare const Base2: abstract new () => A & A2; +>Base2 : Symbol(Base2, Decl(abstractIntersectedClasses1.ts, 31, 13)) +>A : Symbol(A, Decl(abstractIntersectedClasses1.ts, 0, 0)) +>A2 : Symbol(A2, Decl(abstractIntersectedClasses1.ts, 4, 1)) + +class Bar1 extends Base2 {} // error +>Bar1 : Symbol(Bar1, Decl(abstractIntersectedClasses1.ts, 31, 47)) +>Base2 : Symbol(Base2, Decl(abstractIntersectedClasses1.ts, 31, 13)) + +class Bar2 extends Base2 { // ok +>Bar2 : Symbol(Bar2, Decl(abstractIntersectedClasses1.ts, 33, 27)) +>Base2 : Symbol(Base2, Decl(abstractIntersectedClasses1.ts, 31, 13)) + + a() { +>a : Symbol(Bar2.a, Decl(abstractIntersectedClasses1.ts, 34, 26)) + + return 100; + } +} + diff --git a/tests/baselines/reference/abstractIntersectedClasses1.types b/tests/baselines/reference/abstractIntersectedClasses1.types new file mode 100644 index 0000000000000..ac501bb067789 --- /dev/null +++ b/tests/baselines/reference/abstractIntersectedClasses1.types @@ -0,0 +1,107 @@ +//// [tests/cases/compiler/abstractIntersectedClasses1.ts] //// + +=== abstractIntersectedClasses1.ts === +// https://github.com/microsoft/TypeScript/issues/56738 + +abstract class A { +>A : A +> : ^ + + abstract a(): number; +>a : () => number +> : ^^^^^^ +} + +abstract class A2 { +>A2 : A2 +> : ^^ + + abstract a(): number; +>a : () => number +> : ^^^^^^ +} + +abstract class B { +>B : B +> : ^ + + abstract b(): number; +>b : () => number +> : ^^^^^^ +} + +declare const Base: abstract new () => A & B; +>Base : abstract new () => A & B +> : ^^^^^^^^^^^^^^^^^^^ + +class Foo1 extends Base {} // error +>Foo1 : Foo1 +> : ^^^^ +>Base : A & B +> : ^^^^^ + +class Foo2 extends Base { // error +>Foo2 : Foo2 +> : ^^^^ +>Base : A & B +> : ^^^^^ + + a() { +>a : () => number +> : ^^^^^^^^^^^^ + + return 10; +>10 : 10 +> : ^^ + } +} +class Foo3 extends Base { // ok +>Foo3 : Foo3 +> : ^^^^ +>Base : A & B +> : ^^^^^ + + a() { +>a : () => number +> : ^^^^^^^^^^^^ + + return 10; +>10 : 10 +> : ^^ + } + b() { +>b : () => number +> : ^^^^^^^^^^^^ + + return 42; +>42 : 42 +> : ^^ + } +} + +declare const Base2: abstract new () => A & A2; +>Base2 : abstract new () => A & A2 +> : ^^^^^^^^^^^^^^^^^^^ + +class Bar1 extends Base2 {} // error +>Bar1 : Bar1 +> : ^^^^ +>Base2 : A & A2 +> : ^^^^^^ + +class Bar2 extends Base2 { // ok +>Bar2 : Bar2 +> : ^^^^ +>Base2 : A & A2 +> : ^^^^^^ + + a() { +>a : () => number +> : ^^^^^^^^^^^^ + + return 100; +>100 : 100 +> : ^^^ + } +} + diff --git a/tests/baselines/reference/abstractIntersectedClasses2.errors.txt b/tests/baselines/reference/abstractIntersectedClasses2.errors.txt new file mode 100644 index 0000000000000..81597d9d16f84 --- /dev/null +++ b/tests/baselines/reference/abstractIntersectedClasses2.errors.txt @@ -0,0 +1,37 @@ +abstractIntersectedClasses2.ts(24,7): error TS2515: Non-abstract class 'ImplementationB' does not implement inherited abstract member testMethod from class 'MixinB<((abstract new (...args: any[]) => MixinA.Mixin) & { prototype: MixinA.Mixin; }) & typeof Base>.Mixin & MixinA.Mixin & Base'. +abstractIntersectedClasses2.ts(27,7): error TS2515: Non-abstract class 'ImplementationA' does not implement inherited abstract member testMethod from class 'MixinA.Mixin & Base'. + + +==== abstractIntersectedClasses2.ts (2 errors) ==== + // https://github.com/microsoft/TypeScript/issues/62014 + + type Constructor = new (...args: any[]) => {}; + + function MixinA(base: T) { + abstract class Mixin extends base { + abstract testMethod(): string; + } + + return Mixin; + } + + function MixinB(base: T) { + abstract class Mixin extends base { + abstract testMethod(): string; + } + + return Mixin; + } + + class Base {} + + // error + class ImplementationB extends MixinB(MixinA(Base)) {} + ~~~~~~~~~~~~~~~ +!!! error TS2515: Non-abstract class 'ImplementationB' does not implement inherited abstract member testMethod from class 'MixinB<((abstract new (...args: any[]) => MixinA.Mixin) & { prototype: MixinA.Mixin; }) & typeof Base>.Mixin & MixinA.Mixin & Base'. + + // error + class ImplementationA extends MixinA(Base) {} + ~~~~~~~~~~~~~~~ +!!! error TS2515: Non-abstract class 'ImplementationA' does not implement inherited abstract member testMethod from class 'MixinA.Mixin & Base'. + \ No newline at end of file diff --git a/tests/baselines/reference/abstractIntersectedClasses2.symbols b/tests/baselines/reference/abstractIntersectedClasses2.symbols new file mode 100644 index 0000000000000..f7b82f5918eb5 --- /dev/null +++ b/tests/baselines/reference/abstractIntersectedClasses2.symbols @@ -0,0 +1,63 @@ +//// [tests/cases/compiler/abstractIntersectedClasses2.ts] //// + +=== abstractIntersectedClasses2.ts === +// https://github.com/microsoft/TypeScript/issues/62014 + +type Constructor = new (...args: any[]) => {}; +>Constructor : Symbol(Constructor, Decl(abstractIntersectedClasses2.ts, 0, 0)) +>args : Symbol(args, Decl(abstractIntersectedClasses2.ts, 2, 24)) + +function MixinA(base: T) { +>MixinA : Symbol(MixinA, Decl(abstractIntersectedClasses2.ts, 2, 46)) +>T : Symbol(T, Decl(abstractIntersectedClasses2.ts, 4, 16)) +>Constructor : Symbol(Constructor, Decl(abstractIntersectedClasses2.ts, 0, 0)) +>base : Symbol(base, Decl(abstractIntersectedClasses2.ts, 4, 39)) +>T : Symbol(T, Decl(abstractIntersectedClasses2.ts, 4, 16)) + + abstract class Mixin extends base { +>Mixin : Symbol(Mixin, Decl(abstractIntersectedClasses2.ts, 4, 49)) +>base : Symbol(base, Decl(abstractIntersectedClasses2.ts, 4, 39)) + + abstract testMethod(): string; +>testMethod : Symbol(Mixin.testMethod, Decl(abstractIntersectedClasses2.ts, 5, 37)) + } + + return Mixin; +>Mixin : Symbol(Mixin, Decl(abstractIntersectedClasses2.ts, 4, 49)) +} + +function MixinB(base: T) { +>MixinB : Symbol(MixinB, Decl(abstractIntersectedClasses2.ts, 10, 1)) +>T : Symbol(T, Decl(abstractIntersectedClasses2.ts, 12, 16)) +>Constructor : Symbol(Constructor, Decl(abstractIntersectedClasses2.ts, 0, 0)) +>base : Symbol(base, Decl(abstractIntersectedClasses2.ts, 12, 39)) +>T : Symbol(T, Decl(abstractIntersectedClasses2.ts, 12, 16)) + + abstract class Mixin extends base { +>Mixin : Symbol(Mixin, Decl(abstractIntersectedClasses2.ts, 12, 49)) +>base : Symbol(base, Decl(abstractIntersectedClasses2.ts, 12, 39)) + + abstract testMethod(): string; +>testMethod : Symbol(Mixin.testMethod, Decl(abstractIntersectedClasses2.ts, 13, 37)) + } + + return Mixin; +>Mixin : Symbol(Mixin, Decl(abstractIntersectedClasses2.ts, 12, 49)) +} + +class Base {} +>Base : Symbol(Base, Decl(abstractIntersectedClasses2.ts, 18, 1)) + +// error +class ImplementationB extends MixinB(MixinA(Base)) {} +>ImplementationB : Symbol(ImplementationB, Decl(abstractIntersectedClasses2.ts, 20, 13)) +>MixinB : Symbol(MixinB, Decl(abstractIntersectedClasses2.ts, 10, 1)) +>MixinA : Symbol(MixinA, Decl(abstractIntersectedClasses2.ts, 2, 46)) +>Base : Symbol(Base, Decl(abstractIntersectedClasses2.ts, 18, 1)) + +// error +class ImplementationA extends MixinA(Base) {} +>ImplementationA : Symbol(ImplementationA, Decl(abstractIntersectedClasses2.ts, 23, 53)) +>MixinA : Symbol(MixinA, Decl(abstractIntersectedClasses2.ts, 2, 46)) +>Base : Symbol(Base, Decl(abstractIntersectedClasses2.ts, 18, 1)) + diff --git a/tests/baselines/reference/abstractIntersectedClasses2.types b/tests/baselines/reference/abstractIntersectedClasses2.types new file mode 100644 index 0000000000000..c23da7172b742 --- /dev/null +++ b/tests/baselines/reference/abstractIntersectedClasses2.types @@ -0,0 +1,85 @@ +//// [tests/cases/compiler/abstractIntersectedClasses2.ts] //// + +=== abstractIntersectedClasses2.ts === +// https://github.com/microsoft/TypeScript/issues/62014 + +type Constructor = new (...args: any[]) => {}; +>Constructor : Constructor +> : ^^^^^^^^^^^ +>args : any[] +> : ^^^^^ + +function MixinA(base: T) { +>MixinA : (base: T) => ((abstract new (...args: any[]) => Mixin) & { prototype: MixinA.Mixin; }) & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>base : T +> : ^ + + abstract class Mixin extends base { +>Mixin : Mixin +> : ^^^^^ +>base : {} +> : ^^ + + abstract testMethod(): string; +>testMethod : () => string +> : ^^^^^^ + } + + return Mixin; +>Mixin : ((abstract new (...args: any[]) => Mixin) & { prototype: MixinA.Mixin; }) & T +> : ^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} + +function MixinB(base: T) { +>MixinB : (base: T) => ((abstract new (...args: any[]) => Mixin) & { prototype: MixinB.Mixin; }) & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>base : T +> : ^ + + abstract class Mixin extends base { +>Mixin : Mixin +> : ^^^^^ +>base : {} +> : ^^ + + abstract testMethod(): string; +>testMethod : () => string +> : ^^^^^^ + } + + return Mixin; +>Mixin : ((abstract new (...args: any[]) => Mixin) & { prototype: MixinB.Mixin; }) & T +> : ^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +} + +class Base {} +>Base : Base +> : ^^^^ + +// error +class ImplementationB extends MixinB(MixinA(Base)) {} +>ImplementationB : ImplementationB +> : ^^^^^^^^^^^^^^^ +>MixinB(MixinA(Base)) : MixinB<((abstract new (...args: any[]) => MixinA.Mixin) & { prototype: MixinA.Mixin; }) & typeof Base>.Mixin & MixinA.Mixin & Base +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>MixinB : (base: T) => ((abstract new (...args: any[]) => Mixin) & { prototype: MixinB.Mixin; }) & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>MixinA(Base) : ((abstract new (...args: any[]) => MixinA.Mixin) & { prototype: MixinA.Mixin; }) & typeof Base +> : ^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>MixinA : (base: T) => ((abstract new (...args: any[]) => Mixin) & { prototype: MixinA.Mixin; }) & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Base : typeof Base +> : ^^^^^^^^^^^ + +// error +class ImplementationA extends MixinA(Base) {} +>ImplementationA : ImplementationA +> : ^^^^^^^^^^^^^^^ +>MixinA(Base) : MixinA.Mixin & Base +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>MixinA : (base: T) => ((abstract new (...args: any[]) => Mixin) & { prototype: MixinA.Mixin; }) & T +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Base : typeof Base +> : ^^^^^^^^^^^ + diff --git a/tests/cases/compiler/abstractIntersectedClasses1.ts b/tests/cases/compiler/abstractIntersectedClasses1.ts new file mode 100644 index 0000000000000..b12f87945807f --- /dev/null +++ b/tests/cases/compiler/abstractIntersectedClasses1.ts @@ -0,0 +1,42 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/56738 + +abstract class A { + abstract a(): number; +} + +abstract class A2 { + abstract a(): number; +} + +abstract class B { + abstract b(): number; +} + +declare const Base: abstract new () => A & B; + +class Foo1 extends Base {} // error +class Foo2 extends Base { // error + a() { + return 10; + } +} +class Foo3 extends Base { // ok + a() { + return 10; + } + b() { + return 42; + } +} + +declare const Base2: abstract new () => A & A2; + +class Bar1 extends Base2 {} // error +class Bar2 extends Base2 { // ok + a() { + return 100; + } +} diff --git a/tests/cases/compiler/abstractIntersectedClasses2.ts b/tests/cases/compiler/abstractIntersectedClasses2.ts new file mode 100644 index 0000000000000..6ca01f5a619ce --- /dev/null +++ b/tests/cases/compiler/abstractIntersectedClasses2.ts @@ -0,0 +1,30 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/62014 + +type Constructor = new (...args: any[]) => {}; + +function MixinA(base: T) { + abstract class Mixin extends base { + abstract testMethod(): string; + } + + return Mixin; +} + +function MixinB(base: T) { + abstract class Mixin extends base { + abstract testMethod(): string; + } + + return Mixin; +} + +class Base {} + +// error +class ImplementationB extends MixinB(MixinA(Base)) {} + +// error +class ImplementationA extends MixinA(Base) {}