diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 51c9493b61721..73fb7af6a4609 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13212,6 +13212,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getBaseTypes(type: InterfaceType): BaseType[] { + if (!(getObjectFlags(type) & (ObjectFlags.ClassOrInterface | ObjectFlags.Reference))) { + return emptyArray; + } if (!type.baseTypesResolved) { if (pushTypeResolution(type, TypeSystemPropertyName.ResolvedBaseTypes)) { if (type.objectFlags & ObjectFlags.Tuple) { @@ -35053,28 +35056,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * In that case we won't consider it used before its declaration, because it gets its value from the superclass' declaration. */ function isPropertyDeclaredInAncestorClass(prop: Symbol): boolean { - if (!(prop.parent!.flags & SymbolFlags.Class)) { - return false; - } - let classType: InterfaceType | undefined = getTypeOfSymbol(prop.parent!) as InterfaceType; - while (true) { - classType = classType.symbol && getSuperClass(classType) as InterfaceType | undefined; - if (!classType) { - return false; - } - const superProperty = getPropertyOfType(classType, prop.escapedName); - if (superProperty && superProperty.valueDeclaration) { - return true; + if (prop.parent && prop.parent.flags & SymbolFlags.Class) { + const baseTypes = getBaseTypes(getDeclaredTypeOfSymbol(prop.parent) as InterfaceType); + if (baseTypes.length) { + const superProperty = getPropertyOfType(baseTypes[0], prop.escapedName); + return !!(superProperty && superProperty.valueDeclaration); } } - } - - function getSuperClass(classType: InterfaceType): Type | undefined { - const x = getBaseTypes(classType); - if (x.length === 0) { - return undefined; - } - return getIntersectionType(x); + return false; } function reportNonexistentProperty(propNode: Identifier | PrivateIdentifier, containingType: Type, isUncheckedJS: boolean) { diff --git a/tests/baselines/reference/checkInheritedProperty.errors.txt b/tests/baselines/reference/checkInheritedProperty.errors.txt new file mode 100644 index 0000000000000..86c262303dd4d --- /dev/null +++ b/tests/baselines/reference/checkInheritedProperty.errors.txt @@ -0,0 +1,17 @@ +checkInheritedProperty.ts(7,14): error TS2729: Property 'b' is used before its initialization. + + +==== checkInheritedProperty.ts (1 errors) ==== + class Base { + } + + declare const BaseFactory: new() => Base & { c: string } + + class Derived extends BaseFactory { + a = this.b + ~ +!!! error TS2729: Property 'b' is used before its initialization. +!!! related TS2728 checkInheritedProperty.ts:8:5: 'b' is declared here. + b = "abc" + } + \ No newline at end of file diff --git a/tests/baselines/reference/checkInheritedProperty.symbols b/tests/baselines/reference/checkInheritedProperty.symbols new file mode 100644 index 0000000000000..c977d078ae24b --- /dev/null +++ b/tests/baselines/reference/checkInheritedProperty.symbols @@ -0,0 +1,26 @@ +//// [tests/cases/compiler/checkInheritedProperty.ts] //// + +=== checkInheritedProperty.ts === +class Base { +>Base : Symbol(Base, Decl(checkInheritedProperty.ts, 0, 0)) +} + +declare const BaseFactory: new() => Base & { c: string } +>BaseFactory : Symbol(BaseFactory, Decl(checkInheritedProperty.ts, 3, 13)) +>Base : Symbol(Base, Decl(checkInheritedProperty.ts, 0, 0)) +>c : Symbol(c, Decl(checkInheritedProperty.ts, 3, 44)) + +class Derived extends BaseFactory { +>Derived : Symbol(Derived, Decl(checkInheritedProperty.ts, 3, 56)) +>BaseFactory : Symbol(BaseFactory, Decl(checkInheritedProperty.ts, 3, 13)) + + a = this.b +>a : Symbol(Derived.a, Decl(checkInheritedProperty.ts, 5, 35)) +>this.b : Symbol(Derived.b, Decl(checkInheritedProperty.ts, 6, 14)) +>this : Symbol(Derived, Decl(checkInheritedProperty.ts, 3, 56)) +>b : Symbol(Derived.b, Decl(checkInheritedProperty.ts, 6, 14)) + + b = "abc" +>b : Symbol(Derived.b, Decl(checkInheritedProperty.ts, 6, 14)) +} + diff --git a/tests/baselines/reference/checkInheritedProperty.types b/tests/baselines/reference/checkInheritedProperty.types new file mode 100644 index 0000000000000..5abc92638b5a5 --- /dev/null +++ b/tests/baselines/reference/checkInheritedProperty.types @@ -0,0 +1,37 @@ +//// [tests/cases/compiler/checkInheritedProperty.ts] //// + +=== checkInheritedProperty.ts === +class Base { +>Base : Base +> : ^^^^ +} + +declare const BaseFactory: new() => Base & { c: string } +>BaseFactory : new () => Base & { c: string; } +> : ^^^^^^^^^^ +>c : string +> : ^^^^^^ + +class Derived extends BaseFactory { +>Derived : Derived +> : ^^^^^^^ +>BaseFactory : Base & { c: string; } +> : ^^^^^^^^^^^^ ^^^ + + a = this.b +>a : string +> : ^^^^^^ +>this.b : string +> : ^^^^^^ +>this : this +> : ^^^^ +>b : string +> : ^^^^^^ + + b = "abc" +>b : string +> : ^^^^^^ +>"abc" : "abc" +> : ^^^^^ +} + diff --git a/tests/baselines/reference/noCrashOnMixin2.errors.txt b/tests/baselines/reference/noCrashOnMixin2.errors.txt new file mode 100644 index 0000000000000..d7e28ae49b003 --- /dev/null +++ b/tests/baselines/reference/noCrashOnMixin2.errors.txt @@ -0,0 +1,41 @@ +noCrashOnMixin2.ts(11,33): error TS2370: A rest parameter must be of an array type. +noCrashOnMixin2.ts(11,40): error TS1047: A rest parameter cannot be optional. +noCrashOnMixin2.ts(14,12): error TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'. +noCrashOnMixin2.ts(23,9): error TS2674: Constructor of class 'Abstract' is protected and only accessible within the class declaration. + + +==== noCrashOnMixin2.ts (4 errors) ==== + // https://github.com/microsoft/TypeScript/issues/62921 + + class Abstract { + protected constructor() { + } + } + + class Concrete extends Abstract { + } + + type Constructor = new (...args?: any[]) => T; + ~~~~~~~~~~~~~~~ +!!! error TS2370: A rest parameter must be of an array type. + ~ +!!! error TS1047: A rest parameter cannot be optional. + + function Mixin(Base: TBase) { + return class extends Base { + ~~~~~ +!!! error TS2545: A mixin class must have a constructor with a single rest parameter of type 'any[]'. + }; + } + + 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. + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/noCrashOnMixin2.symbols b/tests/baselines/reference/noCrashOnMixin2.symbols new file mode 100644 index 0000000000000..428e2e5de280f --- /dev/null +++ b/tests/baselines/reference/noCrashOnMixin2.symbols @@ -0,0 +1,53 @@ +//// [tests/cases/compiler/noCrashOnMixin2.ts] //// + +=== noCrashOnMixin2.ts === +// https://github.com/microsoft/TypeScript/issues/62921 + +class Abstract { +>Abstract : Symbol(Abstract, Decl(noCrashOnMixin2.ts, 0, 0)) + + protected constructor() { + } +} + +class Concrete extends Abstract { +>Concrete : Symbol(Concrete, Decl(noCrashOnMixin2.ts, 5, 1)) +>Abstract : Symbol(Abstract, Decl(noCrashOnMixin2.ts, 0, 0)) +} + +type Constructor = new (...args?: any[]) => T; +>Constructor : Symbol(Constructor, Decl(noCrashOnMixin2.ts, 8, 1)) +>T : Symbol(T, Decl(noCrashOnMixin2.ts, 10, 17)) +>args : Symbol(args, Decl(noCrashOnMixin2.ts, 10, 32)) +>T : Symbol(T, Decl(noCrashOnMixin2.ts, 10, 17)) + +function Mixin(Base: TBase) { +>Mixin : Symbol(Mixin, Decl(noCrashOnMixin2.ts, 10, 54)) +>TBase : Symbol(TBase, Decl(noCrashOnMixin2.ts, 12, 15)) +>Constructor : Symbol(Constructor, Decl(noCrashOnMixin2.ts, 8, 1)) +>Base : Symbol(Base, Decl(noCrashOnMixin2.ts, 12, 42)) +>TBase : Symbol(TBase, Decl(noCrashOnMixin2.ts, 12, 15)) + + return class extends Base { +>Base : Symbol(Base, Decl(noCrashOnMixin2.ts, 12, 42)) + + }; +} + +class Empty { +>Empty : Symbol(Empty, Decl(noCrashOnMixin2.ts, 15, 1)) +} + +class CrashTrigger extends Mixin(Empty) { +>CrashTrigger : Symbol(CrashTrigger, Decl(noCrashOnMixin2.ts, 18, 1)) +>Mixin : Symbol(Mixin, Decl(noCrashOnMixin2.ts, 10, 54)) +>Empty : Symbol(Empty, Decl(noCrashOnMixin2.ts, 15, 1)) + + public trigger() { +>trigger : Symbol(CrashTrigger.trigger, Decl(noCrashOnMixin2.ts, 20, 41)) + + new Concrete(); +>Concrete : Symbol(Concrete, Decl(noCrashOnMixin2.ts, 5, 1)) + } +} + diff --git a/tests/baselines/reference/noCrashOnMixin2.types b/tests/baselines/reference/noCrashOnMixin2.types new file mode 100644 index 0000000000000..e568789976804 --- /dev/null +++ b/tests/baselines/reference/noCrashOnMixin2.types @@ -0,0 +1,68 @@ +//// [tests/cases/compiler/noCrashOnMixin2.ts] //// + +=== noCrashOnMixin2.ts === +// https://github.com/microsoft/TypeScript/issues/62921 + +class Abstract { +>Abstract : Abstract +> : ^^^^^^^^ + + protected constructor() { + } +} + +class Concrete extends Abstract { +>Concrete : Concrete +> : ^^^^^^^^ +>Abstract : Abstract +> : ^^^^^^^^ +} + +type Constructor = new (...args?: any[]) => T; +>Constructor : Constructor +> : ^^^^^^^^^^^^^^ +>args : any[] | undefined +> : ^^^^^^^^^^^^^^^^^ + +function Mixin(Base: TBase) { +>Mixin : (Base: TBase) => { new (...args?: any[]): (Anonymous class); prototype: Mixin.(Anonymous class); } & TBase +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Base : TBase +> : ^^^^^ + + return class extends Base { +>class extends Base { } : { new (...args?: any[]): (Anonymous class); prototype: Mixin.(Anonymous class); } & TBase +> : ^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Base : {} +> : ^^ + + }; +} + +class Empty { +>Empty : Empty +> : ^^^^^ +} + +class CrashTrigger extends Mixin(Empty) { +>CrashTrigger : CrashTrigger +> : ^^^^^^^^^^^^ +>Mixin(Empty) : Mixin.(Anonymous class) +> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Mixin : (Base: TBase) => { new (...args?: any[]): (Anonymous class); prototype: Mixin.(Anonymous class); } & TBase +> : ^ ^^^^^^^^^ ^^ ^^ ^^^^^^^^^^^^^^^ ^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +>Empty : typeof Empty +> : ^^^^^^^^^^^^ + + public trigger() { +>trigger : () => void +> : ^^^^^^^^^^ + + new Concrete(); +>new Concrete() : any +> : ^^^ +>Concrete : typeof Concrete +> : ^^^^^^^^^^^^^^^ + } +} + diff --git a/tests/cases/compiler/checkInheritedProperty.ts b/tests/cases/compiler/checkInheritedProperty.ts new file mode 100644 index 0000000000000..12960b990c5cc --- /dev/null +++ b/tests/cases/compiler/checkInheritedProperty.ts @@ -0,0 +1,12 @@ +// @strict: true +// @noEmit: true + +class Base { +} + +declare const BaseFactory: new() => Base & { c: string } + +class Derived extends BaseFactory { + a = this.b + b = "abc" +} diff --git a/tests/cases/compiler/noCrashOnMixin2.ts b/tests/cases/compiler/noCrashOnMixin2.ts new file mode 100644 index 0000000000000..f6ddccf9480d2 --- /dev/null +++ b/tests/cases/compiler/noCrashOnMixin2.ts @@ -0,0 +1,28 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/62921 + +class Abstract { + protected constructor() { + } +} + +class Concrete extends Abstract { +} + +type Constructor = new (...args?: any[]) => T; + +function Mixin(Base: TBase) { + return class extends Base { + }; +} + +class Empty { +} + +class CrashTrigger extends Mixin(Empty) { + public trigger() { + new Concrete(); + } +}