diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 65673177efe..2bae0c73fc4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -12069,7 +12069,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { /** * The base constructor of a class can resolve to * * undefinedType if the class has no extends clause, - * * unknownType if an error occurred during resolution of the extends expression, + * * errorType if an error occurred during resolution of the extends expression, * * nullType if the extends expression is the null value, * * anyType if the extends expression has type any, or * * an object type with at least one construct signature. @@ -28817,7 +28817,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * Otherwise, return false * @param classDecl a class declaration to check if it extends null */ - function classDeclarationExtendsNull(classDecl: ClassDeclaration): boolean { + function classDeclarationExtendsNull(classDecl: ClassLikeDeclaration): boolean { const classSymbol = getSymbolOfDeclaration(classDecl); const classInstanceType = getDeclaredTypeOfSymbol(classSymbol) as InterfaceType; const baseConstructorType = getBaseConstructorTypeOfClass(classInstanceType); @@ -29227,6 +29227,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return errorType; } + if (classDeclarationExtendsNull(classLikeDeclaration)) { + return isCallExpression ? errorType : nullWideningType; + } + const classType = getDeclaredTypeOfSymbol(getSymbolOfDeclaration(classLikeDeclaration)) as InterfaceType; const baseClassType = classType && getBaseTypes(classType)[0]; if (!baseClassType) { @@ -39277,7 +39281,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // TS 1.0 spec (April 2014): 8.3.2 // Constructors of classes with no extends clause may not contain super calls, whereas // constructors of derived classes must contain at least one super call somewhere in their function body. - const containingClassDecl = node.parent as ClassDeclaration; + const containingClassDecl = node.parent; if (getClassExtendsHeritageElement(containingClassDecl)) { captureLexicalThis(node.parent, containingClassDecl); const classExtendsNull = classDeclarationExtendsNull(containingClassDecl); @@ -39293,7 +39297,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // or the containing class declares instance member variables with initializers. const superCallShouldBeRootLevel = !emitStandardClassFields && - (some((node.parent as ClassDeclaration).members, isInstancePropertyWithInitializerOrPrivateIdentifierProperty) || + (some(node.parent.members, isInstancePropertyWithInitializerOrPrivateIdentifierProperty) || some(node.parameters, p => hasSyntacticModifier(p, ModifierFlags.ParameterPropertyModifier))); if (superCallShouldBeRootLevel) { diff --git a/tests/baselines/reference/classExtendsNull2.errors.txt b/tests/baselines/reference/classExtendsNull2.errors.txt new file mode 100644 index 00000000000..d9231171757 --- /dev/null +++ b/tests/baselines/reference/classExtendsNull2.errors.txt @@ -0,0 +1,20 @@ +classExtendsNull2.ts(5,7): error TS2417: Class static side 'typeof C' incorrectly extends base class static side 'null'. +classExtendsNull2.ts(7,5): error TS17005: A constructor cannot contain a 'super' call when its class extends 'null'. + + +==== classExtendsNull2.ts (2 errors) ==== + // https://github.com/microsoft/TypeScript/issues/55499 + + interface Base {} + + class C extends null { + ~ +!!! error TS2417: Class static side 'typeof C' incorrectly extends base class static side 'null'. + constructor() { + super(); + ~~~~~~~ +!!! error TS17005: A constructor cannot contain a 'super' call when its class extends 'null'. + } + } + interface C extends Base {} + \ No newline at end of file diff --git a/tests/baselines/reference/classExtendsNull2.symbols b/tests/baselines/reference/classExtendsNull2.symbols new file mode 100644 index 00000000000..644d43cdc32 --- /dev/null +++ b/tests/baselines/reference/classExtendsNull2.symbols @@ -0,0 +1,19 @@ +//// [tests/cases/compiler/classExtendsNull2.ts] //// + +=== classExtendsNull2.ts === +// https://github.com/microsoft/TypeScript/issues/55499 + +interface Base {} +>Base : Symbol(Base, Decl(classExtendsNull2.ts, 0, 0)) + +class C extends null { +>C : Symbol(C, Decl(classExtendsNull2.ts, 2, 17), Decl(classExtendsNull2.ts, 8, 1)) + + constructor() { + super(); + } +} +interface C extends Base {} +>C : Symbol(C, Decl(classExtendsNull2.ts, 2, 17), Decl(classExtendsNull2.ts, 8, 1)) +>Base : Symbol(Base, Decl(classExtendsNull2.ts, 0, 0)) + diff --git a/tests/baselines/reference/classExtendsNull2.types b/tests/baselines/reference/classExtendsNull2.types new file mode 100644 index 00000000000..df5f68aecc8 --- /dev/null +++ b/tests/baselines/reference/classExtendsNull2.types @@ -0,0 +1,18 @@ +//// [tests/cases/compiler/classExtendsNull2.ts] //// + +=== classExtendsNull2.ts === +// https://github.com/microsoft/TypeScript/issues/55499 + +interface Base {} + +class C extends null { +>C : C + + constructor() { + super(); +>super() : void +>super : any + } +} +interface C extends Base {} + diff --git a/tests/baselines/reference/classExtendsNull3.errors.txt b/tests/baselines/reference/classExtendsNull3.errors.txt new file mode 100644 index 00000000000..ced2fb3b69c --- /dev/null +++ b/tests/baselines/reference/classExtendsNull3.errors.txt @@ -0,0 +1,20 @@ +classExtendsNull3.ts(3,5): error TS2531: Object is possibly 'null'. +classExtendsNull3.ts(9,5): error TS2531: Object is possibly 'null'. + + +==== classExtendsNull3.ts (2 errors) ==== + class C1 extends null { + static method() { + super.oops; + ~~~~~ +!!! error TS2531: Object is possibly 'null'. + } + } + + class C2 extends null { + method() { + super.oops; + ~~~~~ +!!! error TS2531: Object is possibly 'null'. + } + } \ No newline at end of file diff --git a/tests/baselines/reference/classExtendsNull3.symbols b/tests/baselines/reference/classExtendsNull3.symbols new file mode 100644 index 00000000000..8b8a259cb81 --- /dev/null +++ b/tests/baselines/reference/classExtendsNull3.symbols @@ -0,0 +1,22 @@ +//// [tests/cases/compiler/classExtendsNull3.ts] //// + +=== classExtendsNull3.ts === +class C1 extends null { +>C1 : Symbol(C1, Decl(classExtendsNull3.ts, 0, 0)) + + static method() { +>method : Symbol(C1.method, Decl(classExtendsNull3.ts, 0, 23)) + + super.oops; + } +} + +class C2 extends null { +>C2 : Symbol(C2, Decl(classExtendsNull3.ts, 4, 1)) + + method() { +>method : Symbol(C2.method, Decl(classExtendsNull3.ts, 6, 23)) + + super.oops; + } +} diff --git a/tests/baselines/reference/classExtendsNull3.types b/tests/baselines/reference/classExtendsNull3.types new file mode 100644 index 00000000000..d58ac944fea --- /dev/null +++ b/tests/baselines/reference/classExtendsNull3.types @@ -0,0 +1,28 @@ +//// [tests/cases/compiler/classExtendsNull3.ts] //// + +=== classExtendsNull3.ts === +class C1 extends null { +>C1 : C1 + + static method() { +>method : () => void + + super.oops; +>super.oops : any +>super : null +>oops : any + } +} + +class C2 extends null { +>C2 : C2 + + method() { +>method : () => void + + super.oops; +>super.oops : any +>super : null +>oops : any + } +} diff --git a/tests/cases/compiler/classExtendsNull2.ts b/tests/cases/compiler/classExtendsNull2.ts new file mode 100644 index 00000000000..aad37d190d5 --- /dev/null +++ b/tests/cases/compiler/classExtendsNull2.ts @@ -0,0 +1,13 @@ +// @strict: true +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/55499 + +interface Base {} + +class C extends null { + constructor() { + super(); + } +} +interface C extends Base {} diff --git a/tests/cases/compiler/classExtendsNull3.ts b/tests/cases/compiler/classExtendsNull3.ts new file mode 100644 index 00000000000..d7720a04c65 --- /dev/null +++ b/tests/cases/compiler/classExtendsNull3.ts @@ -0,0 +1,14 @@ +// @strict: true +// @noEmit: true + +class C1 extends null { + static method() { + super.oops; + } +} + +class C2 extends null { + method() { + super.oops; + } +} \ No newline at end of file