Consistently return errorType for super expressions in classes that extend null (#55506)

This commit is contained in:
Mateusz Burzyński 2023-09-15 04:31:50 +02:00 committed by GitHub
parent 58650d97c5
commit e9af22ace9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 162 additions and 4 deletions

View File

@ -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) {

View File

@ -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 {}

View File

@ -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))

View File

@ -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 {}

View File

@ -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'.
}
}

View File

@ -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;
}
}

View File

@ -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
}
}

View File

@ -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 {}

View File

@ -0,0 +1,14 @@
// @strict: true
// @noEmit: true
class C1 extends null {
static method() {
super.oops;
}
}
class C2 extends null {
method() {
super.oops;
}
}