Error on class fields accesses through super in JS files (#55892)

This commit is contained in:
Mateusz Burzyński 2023-09-29 21:29:39 +02:00 committed by GitHub
parent 9cf44dc51a
commit 485a2ea0c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 638 additions and 1 deletions

View File

@ -102,7 +102,9 @@ import {
isArrowFunction,
isAssignmentExpression,
isBinaryExpression,
isBindableStaticAccessExpression,
isBindableStaticElementAccessExpression,
isBindableStaticNameExpression,
isBindingElement,
isBlock,
isCallExpression,
@ -111,6 +113,7 @@ import {
isClassStaticBlockDeclaration,
isDecorator,
isElementAccessExpression,
isExpandoPropertyDeclaration,
isExportAssignment,
isExportDeclaration,
isExportSpecifier,
@ -153,6 +156,7 @@ import {
isPropertyAccessExpression,
isPropertyAssignment,
isPropertyDeclaration,
isPrototypeAccess,
isRootedDiskPath,
isSourceFile,
isStringLiteral,
@ -1705,7 +1709,10 @@ export function isAutoAccessorPropertyDeclaration(node: Node): node is AutoAcces
}
/** @internal */
export function isClassFieldAndNotAutoAccessor(node: Node): boolean {
export function isClassFieldAndNotAutoAccessor(node: Declaration): boolean {
if (isInJSFile(node) && isExpandoPropertyDeclaration(node)) {
return (!isBindableStaticAccessExpression(node) || !isPrototypeAccess(node.expression)) && !isBindableStaticNameExpression(node, /*excludeThisKeyword*/ true);
}
return node.parent && isClassLike(node.parent) && isPropertyDeclaration(node) && !hasAccessorModifier(node);
}

View File

@ -16,6 +16,15 @@ class C extends Array {
console.log(super.length);
}
}
class D {
accessor b = () => {}
}
class E extends D {
foo() {
super.b()
}
}
//// [classFieldSuperAccessible.js]
@ -35,3 +44,11 @@ class C extends Array {
console.log(super.length);
}
}
class D {
accessor b = () => { };
}
class E extends D {
foo() {
super.b();
}
}

View File

@ -46,3 +46,23 @@ class C extends Array {
}
}
class D {
>D : Symbol(D, Decl(classFieldSuperAccessible.ts, 14, 1))
accessor b = () => {}
>b : Symbol(D.b, Decl(classFieldSuperAccessible.ts, 16, 9))
}
class E extends D {
>E : Symbol(E, Decl(classFieldSuperAccessible.ts, 18, 1))
>D : Symbol(D, Decl(classFieldSuperAccessible.ts, 14, 1))
foo() {
>foo : Symbol(E.foo, Decl(classFieldSuperAccessible.ts, 19, 19))
super.b()
>super.b : Symbol(D.b, Decl(classFieldSuperAccessible.ts, 16, 9))
>super : Symbol(D, Decl(classFieldSuperAccessible.ts, 14, 1))
>b : Symbol(D.b, Decl(classFieldSuperAccessible.ts, 16, 9))
}
}

View File

@ -50,3 +50,25 @@ class C extends Array {
}
}
class D {
>D : D
accessor b = () => {}
>b : () => void
>() => {} : () => void
}
class E extends D {
>E : E
>D : D
foo() {
>foo : () => void
super.b()
>super.b() : void
>super.b : () => void
>super : D
>b : () => void
}
}

View File

@ -0,0 +1,18 @@
index.js(9,23): error TS2565: Property 'blah2' is used before being assigned.
==== index.js (1 errors) ====
class C {
static blah1 = 123;
}
C.blah2 = 456;
class D extends C {
static {
console.log(super.blah1);
console.log(super.blah2);
~~~~~
!!! error TS2565: Property 'blah2' is used before being assigned.
}
}

View File

@ -0,0 +1,37 @@
//// [tests/cases/compiler/classFieldSuperAccessibleJs1.ts] ////
=== index.js ===
class C {
>C : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
static blah1 = 123;
>blah1 : Symbol(C.blah1, Decl(index.js, 0, 9))
}
C.blah2 = 456;
>C.blah2 : Symbol(C.blah2, Decl(index.js, 2, 1))
>C : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
>blah2 : Symbol(C.blah2, Decl(index.js, 2, 1))
class D extends C {
>D : Symbol(D, Decl(index.js, 3, 14))
>C : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
static {
console.log(super.blah1);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>super.blah1 : Symbol(C.blah1, Decl(index.js, 0, 9))
>super : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
>blah1 : Symbol(C.blah1, Decl(index.js, 0, 9))
console.log(super.blah2);
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>super.blah2 : Symbol(C.blah2, Decl(index.js, 2, 1))
>super : Symbol(C, Decl(index.js, 0, 0), Decl(index.js, 2, 1))
>blah2 : Symbol(C.blah2, Decl(index.js, 2, 1))
}
}

View File

@ -0,0 +1,42 @@
//// [tests/cases/compiler/classFieldSuperAccessibleJs1.ts] ////
=== index.js ===
class C {
>C : C
static blah1 = 123;
>blah1 : number
>123 : 123
}
C.blah2 = 456;
>C.blah2 = 456 : 456
>C.blah2 : number
>C : typeof C
>blah2 : number
>456 : 456
class D extends C {
>D : D
>C : C
static {
console.log(super.blah1);
>console.log(super.blah1) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>super.blah1 : number
>super : typeof C
>blah1 : number
console.log(super.blah2);
>console.log(super.blah2) : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>super.blah2 : number
>super : typeof C
>blah2 : number
}
}

View File

@ -0,0 +1,77 @@
//// [tests/cases/compiler/classFieldSuperAccessibleJs2.ts] ////
=== index.js ===
class C {
>C : Symbol(C, Decl(index.js, 0, 0))
constructor() {
this.foo = () => {
>this.foo : Symbol(C.foo, Decl(index.js, 5, 3))
>this : Symbol(C, Decl(index.js, 0, 0))
>foo : Symbol(C.foo, Decl(index.js, 1, 17))
console.log("called arrow");
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
};
}
foo() {
>foo : Symbol(C.foo, Decl(index.js, 5, 3))
console.log("called method");
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
}
}
class D extends C {
>D : Symbol(D, Decl(index.js, 9, 1))
>C : Symbol(C, Decl(index.js, 0, 0))
foo() {
>foo : Symbol(D.foo, Decl(index.js, 11, 19))
console.log("SUPER:");
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
super.foo();
>super.foo : Symbol(C.foo, Decl(index.js, 5, 3))
>super : Symbol(C, Decl(index.js, 0, 0))
>foo : Symbol(C.foo, Decl(index.js, 5, 3))
console.log("THIS:");
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
this.foo();
>this.foo : Symbol(D.foo, Decl(index.js, 11, 19))
>this : Symbol(D, Decl(index.js, 9, 1))
>foo : Symbol(D.foo, Decl(index.js, 11, 19))
}
}
const obj = new D();
>obj : Symbol(obj, Decl(index.js, 20, 5))
>D : Symbol(D, Decl(index.js, 9, 1))
obj.foo();
>obj.foo : Symbol(D.foo, Decl(index.js, 11, 19))
>obj : Symbol(obj, Decl(index.js, 20, 5))
>foo : Symbol(D.foo, Decl(index.js, 11, 19))
D.prototype.foo.call(obj);
>D.prototype.foo.call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
>D.prototype.foo : Symbol(D.foo, Decl(index.js, 11, 19))
>D.prototype : Symbol(D.prototype)
>D : Symbol(D, Decl(index.js, 9, 1))
>prototype : Symbol(D.prototype)
>foo : Symbol(D.foo, Decl(index.js, 11, 19))
>call : Symbol(CallableFunction.call, Decl(lib.es5.d.ts, --, --))
>obj : Symbol(obj, Decl(index.js, 20, 5))

View File

@ -0,0 +1,92 @@
//// [tests/cases/compiler/classFieldSuperAccessibleJs2.ts] ////
=== index.js ===
class C {
>C : C
constructor() {
this.foo = () => {
>this.foo = () => { console.log("called arrow"); } : () => void
>this.foo : () => void
>this : this
>foo : () => void
>() => { console.log("called arrow"); } : () => void
console.log("called arrow");
>console.log("called arrow") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"called arrow" : "called arrow"
};
}
foo() {
>foo : () => void
console.log("called method");
>console.log("called method") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"called method" : "called method"
}
}
class D extends C {
>D : D
>C : C
foo() {
>foo : () => void
console.log("SUPER:");
>console.log("SUPER:") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"SUPER:" : "SUPER:"
super.foo();
>super.foo() : void
>super.foo : () => void
>super : C
>foo : () => void
console.log("THIS:");
>console.log("THIS:") : void
>console.log : (...data: any[]) => void
>console : Console
>log : (...data: any[]) => void
>"THIS:" : "THIS:"
this.foo();
>this.foo() : void
>this.foo : () => void
>this : this
>foo : () => void
}
}
const obj = new D();
>obj : D
>new D() : D
>D : typeof D
obj.foo();
>obj.foo() : void
>obj.foo : () => void
>obj : D
>foo : () => void
D.prototype.foo.call(obj);
>D.prototype.foo.call(obj) : void
>D.prototype.foo.call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
>D.prototype.foo : () => void
>D.prototype : D
>D : typeof D
>prototype : D
>foo : () => void
>call : <T, A extends any[], R>(this: (this: T, ...args: A) => R, thisArg: T, ...args: A) => R
>obj : D

View File

@ -0,0 +1,47 @@
index.js(20,22): error TS2855: Class field 'roots' defined by the parent class is not accessible in the child class via super.
index.js(23,22): error TS2855: Class field 'foo' defined by the parent class is not accessible in the child class via super.
index.js(26,22): error TS2855: Class field 'justProp' defined by the parent class is not accessible in the child class via super.
index.js(29,22): error TS2855: Class field ''literalElementAccess'' defined by the parent class is not accessible in the child class via super.
==== index.js (4 errors) ====
// https://github.com/microsoft/TypeScript/issues/55884
class YaddaBase {
constructor() {
this.roots = "hi";
/** @type number */
this.justProp;
/** @type string */
this['literalElementAccess'];
this.b()
}
accessor b = () => {
this.foo = 10
}
}
class DerivedYadda extends YaddaBase {
get rootTests() {
return super.roots;
~~~~~
!!! error TS2855: Class field 'roots' defined by the parent class is not accessible in the child class via super.
}
get fooTests() {
return super.foo;
~~~
!!! error TS2855: Class field 'foo' defined by the parent class is not accessible in the child class via super.
}
get justPropTests() {
return super.justProp;
~~~~~~~~
!!! error TS2855: Class field 'justProp' defined by the parent class is not accessible in the child class via super.
}
get literalElementAccessTests() {
return super.literalElementAccess;
~~~~~~~~~~~~~~~~~~~~
!!! error TS2855: Class field ''literalElementAccess'' defined by the parent class is not accessible in the child class via super.
}
}

View File

@ -0,0 +1,78 @@
//// [tests/cases/compiler/classFieldSuperNotAccessibleJs.ts] ////
=== index.js ===
// https://github.com/microsoft/TypeScript/issues/55884
class YaddaBase {
>YaddaBase : Symbol(YaddaBase, Decl(index.js, 0, 0))
constructor() {
this.roots = "hi";
>this.roots : Symbol(YaddaBase.roots, Decl(index.js, 3, 19))
>this : Symbol(YaddaBase, Decl(index.js, 0, 0))
>roots : Symbol(YaddaBase.roots, Decl(index.js, 3, 19))
/** @type number */
this.justProp;
>this.justProp : Symbol(YaddaBase.justProp, Decl(index.js, 4, 26))
>this : Symbol(YaddaBase, Decl(index.js, 0, 0))
>justProp : Symbol(YaddaBase.justProp, Decl(index.js, 4, 26))
/** @type string */
this['literalElementAccess'];
>this : Symbol(YaddaBase, Decl(index.js, 0, 0))
>'literalElementAccess' : Symbol(YaddaBase['literalElementAccess'], Decl(index.js, 6, 22))
this.b()
>this.b : Symbol(YaddaBase.b, Decl(index.js, 11, 5))
>this : Symbol(YaddaBase, Decl(index.js, 0, 0))
>b : Symbol(YaddaBase.b, Decl(index.js, 11, 5))
}
accessor b = () => {
>b : Symbol(YaddaBase.b, Decl(index.js, 11, 5))
this.foo = 10
>this.foo : Symbol(YaddaBase.foo, Decl(index.js, 12, 24))
>this : Symbol(YaddaBase, Decl(index.js, 0, 0))
>foo : Symbol(YaddaBase.foo, Decl(index.js, 12, 24))
}
}
class DerivedYadda extends YaddaBase {
>DerivedYadda : Symbol(DerivedYadda, Decl(index.js, 15, 1))
>YaddaBase : Symbol(YaddaBase, Decl(index.js, 0, 0))
get rootTests() {
>rootTests : Symbol(DerivedYadda.rootTests, Decl(index.js, 17, 38))
return super.roots;
>super.roots : Symbol(YaddaBase.roots, Decl(index.js, 3, 19))
>super : Symbol(YaddaBase, Decl(index.js, 0, 0))
>roots : Symbol(YaddaBase.roots, Decl(index.js, 3, 19))
}
get fooTests() {
>fooTests : Symbol(DerivedYadda.fooTests, Decl(index.js, 20, 5))
return super.foo;
>super.foo : Symbol(YaddaBase.foo, Decl(index.js, 12, 24))
>super : Symbol(YaddaBase, Decl(index.js, 0, 0))
>foo : Symbol(YaddaBase.foo, Decl(index.js, 12, 24))
}
get justPropTests() {
>justPropTests : Symbol(DerivedYadda.justPropTests, Decl(index.js, 23, 5))
return super.justProp;
>super.justProp : Symbol(YaddaBase.justProp, Decl(index.js, 4, 26))
>super : Symbol(YaddaBase, Decl(index.js, 0, 0))
>justProp : Symbol(YaddaBase.justProp, Decl(index.js, 4, 26))
}
get literalElementAccessTests() {
>literalElementAccessTests : Symbol(DerivedYadda.literalElementAccessTests, Decl(index.js, 26, 5))
return super.literalElementAccess;
>super.literalElementAccess : Symbol(YaddaBase['literalElementAccess'], Decl(index.js, 6, 22))
>super : Symbol(YaddaBase, Decl(index.js, 0, 0))
>literalElementAccess : Symbol(YaddaBase['literalElementAccess'], Decl(index.js, 6, 22))
}
}

View File

@ -0,0 +1,85 @@
//// [tests/cases/compiler/classFieldSuperNotAccessibleJs.ts] ////
=== index.js ===
// https://github.com/microsoft/TypeScript/issues/55884
class YaddaBase {
>YaddaBase : YaddaBase
constructor() {
this.roots = "hi";
>this.roots = "hi" : "hi"
>this.roots : any
>this : this
>roots : any
>"hi" : "hi"
/** @type number */
this.justProp;
>this.justProp : number
>this : this
>justProp : number
/** @type string */
this['literalElementAccess'];
>this['literalElementAccess'] : any
>this : this
>'literalElementAccess' : "literalElementAccess"
this.b()
>this.b() : void
>this.b : () => void
>this : this
>b : () => void
}
accessor b = () => {
>b : () => void
>() => { this.foo = 10 } : () => void
this.foo = 10
>this.foo = 10 : 10
>this.foo : number | undefined
>this : this
>foo : number | undefined
>10 : 10
}
}
class DerivedYadda extends YaddaBase {
>DerivedYadda : DerivedYadda
>YaddaBase : YaddaBase
get rootTests() {
>rootTests : string
return super.roots;
>super.roots : string
>super : YaddaBase
>roots : string
}
get fooTests() {
>fooTests : number | undefined
return super.foo;
>super.foo : number | undefined
>super : YaddaBase
>foo : number | undefined
}
get justPropTests() {
>justPropTests : number
return super.justProp;
>super.justProp : number
>super : YaddaBase
>justProp : number
}
get literalElementAccessTests() {
>literalElementAccessTests : any
return super.literalElementAccess;
>super.literalElementAccess : any
>super : YaddaBase
>literalElementAccess : any
}
}

View File

@ -14,3 +14,12 @@ class C extends Array {
console.log(super.length);
}
}
class D {
accessor b = () => {}
}
class E extends D {
foo() {
super.b()
}
}

View File

@ -0,0 +1,18 @@
// @strict: true
// @checkJs: true
// @target: esnext
// @noEmit: true
// @filename: index.js
class C {
static blah1 = 123;
}
C.blah2 = 456;
class D extends C {
static {
console.log(super.blah1);
console.log(super.blah2);
}
}

View File

@ -0,0 +1,30 @@
// @strict: true
// @checkJs: true
// @target: esnext
// @noEmit: true
// @filename: index.js
class C {
constructor() {
this.foo = () => {
console.log("called arrow");
};
}
foo() {
console.log("called method");
}
}
class D extends C {
foo() {
console.log("SUPER:");
super.foo();
console.log("THIS:");
this.foo();
}
}
const obj = new D();
obj.foo();
D.prototype.foo.call(obj);

View File

@ -0,0 +1,38 @@
// @strict: true
// @checkJs: true
// @target: esnext
// @noEmit: true
// @filename: index.js
// https://github.com/microsoft/TypeScript/issues/55884
class YaddaBase {
constructor() {
this.roots = "hi";
/** @type number */
this.justProp;
/** @type string */
this['literalElementAccess'];
this.b()
}
accessor b = () => {
this.foo = 10
}
}
class DerivedYadda extends YaddaBase {
get rootTests() {
return super.roots;
}
get fooTests() {
return super.foo;
}
get justPropTests() {
return super.justProp;
}
get literalElementAccessTests() {
return super.literalElementAccess;
}
}