mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-05 08:11:30 -06:00
Fix use-before-def errors for ESNext property declarations (#36465)
* Fix use-before-def errors for ESNext property declarations Fixes #36441 Fixes #36442 * Handle property declarations in nested classes
This commit is contained in:
parent
8a0b8822b2
commit
c1e45ac8af
@ -1327,12 +1327,14 @@ namespace ts {
|
||||
}
|
||||
else if (isPropertyDeclaration(declaration)) {
|
||||
// still might be illegal if a self-referencing property initializer (eg private x = this.x)
|
||||
return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage);
|
||||
return !isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ false);
|
||||
}
|
||||
else if (isParameterPropertyDeclaration(declaration, declaration.parent)) {
|
||||
const container = getEnclosingBlockScopeContainer(declaration.parent);
|
||||
// foo = this.bar is illegal in esnext+useDefineForClassFields when bar is a parameter property
|
||||
return !(compilerOptions.target === ScriptTarget.ESNext && !!compilerOptions.useDefineForClassFields
|
||||
&& isUsedInFunctionOrInstanceProperty(usage, declaration));
|
||||
&& getContainingClass(declaration) === getContainingClass(usage)
|
||||
&& isUsedInFunctionOrInstanceProperty(usage, declaration, container));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -1358,10 +1360,19 @@ namespace ts {
|
||||
}
|
||||
|
||||
const container = getEnclosingBlockScopeContainer(declaration);
|
||||
return !!(usage.flags & NodeFlags.JSDoc)
|
||||
|| isInTypeQuery(usage)
|
||||
|| isUsedInFunctionOrInstanceProperty(usage, declaration, container)
|
||||
&& !(compilerOptions.target === ScriptTarget.ESNext && !!compilerOptions.useDefineForClassFields);
|
||||
if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage)) {
|
||||
return true;
|
||||
}
|
||||
if (isUsedInFunctionOrInstanceProperty(usage, declaration, container)) {
|
||||
if (compilerOptions.target === ScriptTarget.ESNext && !!compilerOptions.useDefineForClassFields) {
|
||||
return (isPropertyDeclaration(declaration) || isParameterPropertyDeclaration(declaration, declaration.parent)) &&
|
||||
!isPropertyImmediatelyReferencedWithinDeclaration(declaration, usage, /*stopAtAnyPropertyDeclaration*/ true);
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
|
||||
const container = getEnclosingBlockScopeContainer(declaration);
|
||||
@ -1413,7 +1424,8 @@ namespace ts {
|
||||
});
|
||||
}
|
||||
|
||||
function isPropertyImmediatelyReferencedWithinDeclaration(declaration: PropertyDeclaration, usage: Node) {
|
||||
/** stopAtAnyPropertyDeclaration is used for detecting ES-standard class field use-before-def errors */
|
||||
function isPropertyImmediatelyReferencedWithinDeclaration(declaration: PropertyDeclaration | ParameterPropertyDeclaration, usage: Node, stopAtAnyPropertyDeclaration: boolean) {
|
||||
// always legal if usage is after declaration
|
||||
if (usage.end > declaration.end) {
|
||||
return false;
|
||||
@ -1428,8 +1440,13 @@ namespace ts {
|
||||
|
||||
switch (node.kind) {
|
||||
case SyntaxKind.ArrowFunction:
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
return true;
|
||||
case SyntaxKind.PropertyDeclaration:
|
||||
// even when stopping at any property declaration, they need to come from the same class
|
||||
return stopAtAnyPropertyDeclaration &&
|
||||
(isPropertyDeclaration(declaration) && node.parent === declaration.parent
|
||||
|| isParameterPropertyDeclaration(declaration, declaration.parent) && node.parent === declaration.parent.parent)
|
||||
? "quit": true;
|
||||
case SyntaxKind.Block:
|
||||
switch (node.parent.kind) {
|
||||
case SyntaxKind.GetAccessor:
|
||||
|
||||
@ -18,7 +18,7 @@ tests/cases/conformance/classes/propertyMemberDeclarations/assignParameterProper
|
||||
m1() {
|
||||
this.foo // ok
|
||||
}
|
||||
constructor(private foo: string) {}
|
||||
constructor(public foo: string) {}
|
||||
quim = this.baz // should error
|
||||
~~~
|
||||
!!! error TS2729: Property 'baz' is used before its initialization.
|
||||
@ -32,4 +32,27 @@ tests/cases/conformance/classes/propertyMemberDeclarations/assignParameterProper
|
||||
this.foo // ok
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
quill = this.foo // ok
|
||||
}
|
||||
|
||||
class E {
|
||||
bar = () => this.foo1 + this.foo2; // both ok
|
||||
foo1 = '';
|
||||
constructor(public foo2: string) {}
|
||||
}
|
||||
|
||||
class F {
|
||||
Inner = class extends F {
|
||||
p2 = this.p1
|
||||
}
|
||||
p1 = 0
|
||||
}
|
||||
class G {
|
||||
Inner = class extends G {
|
||||
p2 = this.p1
|
||||
}
|
||||
constructor(public p1: number) {}
|
||||
}
|
||||
|
||||
@ -6,7 +6,7 @@ class C {
|
||||
m1() {
|
||||
this.foo // ok
|
||||
}
|
||||
constructor(private foo: string) {}
|
||||
constructor(public foo: string) {}
|
||||
quim = this.baz // should error
|
||||
baz = this.foo; // should error
|
||||
quid = this.baz // ok
|
||||
@ -14,6 +14,29 @@ class C {
|
||||
this.foo // ok
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
quill = this.foo // ok
|
||||
}
|
||||
|
||||
class E {
|
||||
bar = () => this.foo1 + this.foo2; // both ok
|
||||
foo1 = '';
|
||||
constructor(public foo2: string) {}
|
||||
}
|
||||
|
||||
class F {
|
||||
Inner = class extends F {
|
||||
p2 = this.p1
|
||||
}
|
||||
p1 = 0
|
||||
}
|
||||
class G {
|
||||
Inner = class extends G {
|
||||
p2 = this.p1
|
||||
}
|
||||
constructor(public p1: number) {}
|
||||
}
|
||||
|
||||
|
||||
//// [assignParameterPropertyToPropertyDeclarationESNext.js]
|
||||
@ -35,3 +58,29 @@ class C {
|
||||
this.foo; // ok
|
||||
}
|
||||
}
|
||||
class D extends C {
|
||||
quill = this.foo; // ok
|
||||
}
|
||||
class E {
|
||||
foo2;
|
||||
bar = () => this.foo1 + this.foo2; // both ok
|
||||
foo1 = '';
|
||||
constructor(foo2) {
|
||||
this.foo2 = foo2;
|
||||
}
|
||||
}
|
||||
class F {
|
||||
Inner = class extends F {
|
||||
p2 = this.p1;
|
||||
};
|
||||
p1 = 0;
|
||||
}
|
||||
class G {
|
||||
p1;
|
||||
Inner = class extends G {
|
||||
p2 = this.p1;
|
||||
};
|
||||
constructor(p1) {
|
||||
this.p1 = p1;
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,11 +28,11 @@ class C {
|
||||
>this : Symbol(C, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 0, 0))
|
||||
>foo : Symbol(C.foo, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 7, 16))
|
||||
}
|
||||
constructor(private foo: string) {}
|
||||
constructor(public foo: string) {}
|
||||
>foo : Symbol(C.foo, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 7, 16))
|
||||
|
||||
quim = this.baz // should error
|
||||
>quim : Symbol(C.quim, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 7, 39))
|
||||
>quim : Symbol(C.quim, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 7, 38))
|
||||
>this.baz : Symbol(C.baz, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 8, 19))
|
||||
>this : Symbol(C, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 0, 0))
|
||||
>baz : Symbol(C.baz, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 8, 19))
|
||||
@ -59,3 +59,66 @@ class C {
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
>D : Symbol(D, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 14, 1))
|
||||
>C : Symbol(C, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 0, 0))
|
||||
|
||||
quill = this.foo // ok
|
||||
>quill : Symbol(D.quill, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 16, 19))
|
||||
>this.foo : Symbol(C.foo, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 7, 16))
|
||||
>this : Symbol(D, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 14, 1))
|
||||
>foo : Symbol(C.foo, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 7, 16))
|
||||
}
|
||||
|
||||
class E {
|
||||
>E : Symbol(E, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 18, 1))
|
||||
|
||||
bar = () => this.foo1 + this.foo2; // both ok
|
||||
>bar : Symbol(E.bar, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 20, 9))
|
||||
>this.foo1 : Symbol(E.foo1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 21, 38))
|
||||
>this : Symbol(E, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 18, 1))
|
||||
>foo1 : Symbol(E.foo1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 21, 38))
|
||||
>this.foo2 : Symbol(E.foo2, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 23, 16))
|
||||
>this : Symbol(E, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 18, 1))
|
||||
>foo2 : Symbol(E.foo2, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 23, 16))
|
||||
|
||||
foo1 = '';
|
||||
>foo1 : Symbol(E.foo1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 21, 38))
|
||||
|
||||
constructor(public foo2: string) {}
|
||||
>foo2 : Symbol(E.foo2, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 23, 16))
|
||||
}
|
||||
|
||||
class F {
|
||||
>F : Symbol(F, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 24, 1))
|
||||
|
||||
Inner = class extends F {
|
||||
>Inner : Symbol(F.Inner, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 26, 9))
|
||||
>F : Symbol(F, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 24, 1))
|
||||
|
||||
p2 = this.p1
|
||||
>p2 : Symbol((Anonymous class).p2, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 27, 29))
|
||||
>this.p1 : Symbol(F.p1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 29, 5))
|
||||
>this : Symbol((Anonymous class), Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 27, 11))
|
||||
>p1 : Symbol(F.p1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 29, 5))
|
||||
}
|
||||
p1 = 0
|
||||
>p1 : Symbol(F.p1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 29, 5))
|
||||
}
|
||||
class G {
|
||||
>G : Symbol(G, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 31, 1))
|
||||
|
||||
Inner = class extends G {
|
||||
>Inner : Symbol(G.Inner, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 32, 9))
|
||||
>G : Symbol(G, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 31, 1))
|
||||
|
||||
p2 = this.p1
|
||||
>p2 : Symbol((Anonymous class).p2, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 33, 29))
|
||||
>this.p1 : Symbol(G.p1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 36, 16))
|
||||
>this : Symbol((Anonymous class), Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 33, 11))
|
||||
>p1 : Symbol(G.p1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 36, 16))
|
||||
}
|
||||
constructor(public p1: number) {}
|
||||
>p1 : Symbol(G.p1, Decl(assignParameterPropertyToPropertyDeclarationESNext.ts, 36, 16))
|
||||
}
|
||||
|
||||
|
||||
@ -28,7 +28,7 @@ class C {
|
||||
>this : this
|
||||
>foo : string
|
||||
}
|
||||
constructor(private foo: string) {}
|
||||
constructor(public foo: string) {}
|
||||
>foo : string
|
||||
|
||||
quim = this.baz // should error
|
||||
@ -59,3 +59,72 @@ class C {
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
>D : D
|
||||
>C : C
|
||||
|
||||
quill = this.foo // ok
|
||||
>quill : string
|
||||
>this.foo : string
|
||||
>this : this
|
||||
>foo : string
|
||||
}
|
||||
|
||||
class E {
|
||||
>E : E
|
||||
|
||||
bar = () => this.foo1 + this.foo2; // both ok
|
||||
>bar : () => string
|
||||
>() => this.foo1 + this.foo2 : () => string
|
||||
>this.foo1 + this.foo2 : string
|
||||
>this.foo1 : string
|
||||
>this : this
|
||||
>foo1 : string
|
||||
>this.foo2 : string
|
||||
>this : this
|
||||
>foo2 : string
|
||||
|
||||
foo1 = '';
|
||||
>foo1 : string
|
||||
>'' : ""
|
||||
|
||||
constructor(public foo2: string) {}
|
||||
>foo2 : string
|
||||
}
|
||||
|
||||
class F {
|
||||
>F : F
|
||||
|
||||
Inner = class extends F {
|
||||
>Inner : typeof (Anonymous class)
|
||||
>class extends F { p2 = this.p1 } : typeof (Anonymous class)
|
||||
>F : F
|
||||
|
||||
p2 = this.p1
|
||||
>p2 : number
|
||||
>this.p1 : number
|
||||
>this : this
|
||||
>p1 : number
|
||||
}
|
||||
p1 = 0
|
||||
>p1 : number
|
||||
>0 : 0
|
||||
}
|
||||
class G {
|
||||
>G : G
|
||||
|
||||
Inner = class extends G {
|
||||
>Inner : typeof (Anonymous class)
|
||||
>class extends G { p2 = this.p1 } : typeof (Anonymous class)
|
||||
>G : G
|
||||
|
||||
p2 = this.p1
|
||||
>p2 : number
|
||||
>this.p1 : number
|
||||
>this : this
|
||||
>p1 : number
|
||||
}
|
||||
constructor(public p1: number) {}
|
||||
>p1 : number
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ class C {
|
||||
m1() {
|
||||
this.foo // ok
|
||||
}
|
||||
constructor(private foo: string) {}
|
||||
constructor(public foo: string) {}
|
||||
quim = this.baz // should error
|
||||
baz = this.foo; // should error
|
||||
quid = this.baz // ok
|
||||
@ -15,3 +15,26 @@ class C {
|
||||
this.foo // ok
|
||||
}
|
||||
}
|
||||
|
||||
class D extends C {
|
||||
quill = this.foo // ok
|
||||
}
|
||||
|
||||
class E {
|
||||
bar = () => this.foo1 + this.foo2; // both ok
|
||||
foo1 = '';
|
||||
constructor(public foo2: string) {}
|
||||
}
|
||||
|
||||
class F {
|
||||
Inner = class extends F {
|
||||
p2 = this.p1
|
||||
}
|
||||
p1 = 0
|
||||
}
|
||||
class G {
|
||||
Inner = class extends G {
|
||||
p2 = this.p1
|
||||
}
|
||||
constructor(public p1: number) {}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user