mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-15 03:23:08 -06:00
Merge pull request #13914 from Microsoft/forward-ref-in-property-initialisers
Error on forward references in property initializers
This commit is contained in:
commit
94aeff2a2c
@ -641,7 +641,7 @@ namespace ts {
|
||||
}
|
||||
// declaration is after usage
|
||||
// can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
|
||||
if (isUsedInFunctionOrNonStaticProperty(usage)) {
|
||||
if (isUsedInFunctionOrInstanceProperty(usage)) {
|
||||
return true;
|
||||
}
|
||||
const sourceFiles = host.getSourceFiles();
|
||||
@ -668,10 +668,12 @@ namespace ts {
|
||||
}
|
||||
|
||||
|
||||
// declaration is after usage
|
||||
// can be legal if usage is deferred (i.e. inside function or in initializer of instance property)
|
||||
// declaration is after usage, but it can still be legal if usage is deferred:
|
||||
// 1. inside a function
|
||||
// 2. inside an instance property initializer, a reference to a non-instance property
|
||||
const container = getEnclosingBlockScopeContainer(declaration);
|
||||
return isUsedInFunctionOrNonStaticProperty(usage, container);
|
||||
const isInstanceProperty = declaration.kind === SyntaxKind.PropertyDeclaration && !(getModifierFlags(declaration) & ModifierFlags.Static);
|
||||
return isUsedInFunctionOrInstanceProperty(usage, isInstanceProperty, container);
|
||||
|
||||
function isImmediatelyUsedInInitializerOfBlockScopedVariable(declaration: VariableDeclaration, usage: Node): boolean {
|
||||
const container = getEnclosingBlockScopeContainer(declaration);
|
||||
@ -700,7 +702,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isUsedInFunctionOrNonStaticProperty(usage: Node, container?: Node): boolean {
|
||||
function isUsedInFunctionOrInstanceProperty(usage: Node, isDeclarationInstanceProperty?: boolean, container?: Node): boolean {
|
||||
let current = usage;
|
||||
while (current) {
|
||||
if (current === container) {
|
||||
@ -711,13 +713,13 @@ namespace ts {
|
||||
return true;
|
||||
}
|
||||
|
||||
const initializerOfNonStaticProperty = current.parent &&
|
||||
const initializerOfInstanceProperty = current.parent &&
|
||||
current.parent.kind === SyntaxKind.PropertyDeclaration &&
|
||||
(getModifierFlags(current.parent) & ModifierFlags.Static) === 0 &&
|
||||
(<PropertyDeclaration>current.parent).initializer === current;
|
||||
|
||||
if (initializerOfNonStaticProperty) {
|
||||
return true;
|
||||
if (initializerOfInstanceProperty) {
|
||||
return !isDeclarationInstanceProperty;
|
||||
}
|
||||
|
||||
current = current.parent;
|
||||
@ -986,10 +988,10 @@ namespace ts {
|
||||
// interface bar {}
|
||||
// }
|
||||
// const foo/*1*/: foo/*2*/.bar;
|
||||
// The foo at /*1*/ and /*2*/ will share same symbol with two meaning
|
||||
// block - scope variable and namespace module. However, only when we
|
||||
// The foo at /*1*/ and /*2*/ will share same symbol with two meanings:
|
||||
// block-scoped variable and namespace module. However, only when we
|
||||
// try to resolve name in /*1*/ which is used in variable position,
|
||||
// we want to check for block- scoped
|
||||
// we want to check for block-scoped
|
||||
if (meaning & SymbolFlags.BlockScopedVariable) {
|
||||
const exportOrLocalSymbol = getExportSymbolOfValueSymbolIfExported(result);
|
||||
if (exportOrLocalSymbol.flags & SymbolFlags.BlockScopedVariable) {
|
||||
@ -1013,7 +1015,7 @@ namespace ts {
|
||||
return false;
|
||||
}
|
||||
|
||||
const container = getThisContainer(errorLocation, /* includeArrowFunctions */ true);
|
||||
const container = getThisContainer(errorLocation, /*includeArrowFunctions*/ true);
|
||||
let location = container;
|
||||
while (location) {
|
||||
if (isClassLike(location.parent)) {
|
||||
@ -12543,6 +12545,16 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
function isInPropertyInitializer(node: Node): boolean {
|
||||
while (node) {
|
||||
if (node.parent && node.parent.kind === SyntaxKind.PropertyDeclaration && (node.parent as PropertyDeclaration).initializer === node) {
|
||||
return true;
|
||||
}
|
||||
node = node.parent;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, right: Identifier) {
|
||||
const type = checkNonNullExpression(left);
|
||||
if (isTypeAny(type) || type === silentNeverType) {
|
||||
@ -12565,6 +12577,11 @@ namespace ts {
|
||||
}
|
||||
return unknownType;
|
||||
}
|
||||
if (prop.valueDeclaration &&
|
||||
isInPropertyInitializer(node) &&
|
||||
!isBlockScopedNameDeclaredBeforeUse(prop.valueDeclaration, right)) {
|
||||
error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, right.text);
|
||||
}
|
||||
|
||||
markPropertyAsReferenced(prop);
|
||||
|
||||
|
||||
@ -0,0 +1,27 @@
|
||||
tests/cases/compiler/forwardRefInClassProperties.ts(3,15): error TS2448: Block-scoped variable '_a' used before its declaration.
|
||||
tests/cases/compiler/forwardRefInClassProperties.ts(6,22): error TS2448: Block-scoped variable '_A' used before its declaration.
|
||||
tests/cases/compiler/forwardRefInClassProperties.ts(11,17): error TS2448: Block-scoped variable 'b' used before its declaration.
|
||||
|
||||
|
||||
==== tests/cases/compiler/forwardRefInClassProperties.ts (3 errors) ====
|
||||
class Test
|
||||
{
|
||||
_b = this._a; // undefined, no error/warning
|
||||
~~
|
||||
!!! error TS2448: Block-scoped variable '_a' used before its declaration.
|
||||
_a = 3;
|
||||
|
||||
static _B = Test._A; // undefined, no error/warning
|
||||
~~
|
||||
!!! error TS2448: Block-scoped variable '_A' used before its declaration.
|
||||
static _A = 3;
|
||||
|
||||
method()
|
||||
{
|
||||
let a = b; // Block-scoped variable 'b' used before its declaration
|
||||
~
|
||||
!!! error TS2448: Block-scoped variable 'b' used before its declaration.
|
||||
let b = 3;
|
||||
}
|
||||
}
|
||||
|
||||
31
tests/baselines/reference/forwardRefInClassProperties.js
Normal file
31
tests/baselines/reference/forwardRefInClassProperties.js
Normal file
@ -0,0 +1,31 @@
|
||||
//// [forwardRefInClassProperties.ts]
|
||||
class Test
|
||||
{
|
||||
_b = this._a; // undefined, no error/warning
|
||||
_a = 3;
|
||||
|
||||
static _B = Test._A; // undefined, no error/warning
|
||||
static _A = 3;
|
||||
|
||||
method()
|
||||
{
|
||||
let a = b; // Block-scoped variable 'b' used before its declaration
|
||||
let b = 3;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//// [forwardRefInClassProperties.js]
|
||||
var Test = (function () {
|
||||
function Test() {
|
||||
this._b = this._a; // undefined, no error/warning
|
||||
this._a = 3;
|
||||
}
|
||||
Test.prototype.method = function () {
|
||||
var a = b; // Block-scoped variable 'b' used before its declaration
|
||||
var b = 3;
|
||||
};
|
||||
return Test;
|
||||
}());
|
||||
Test._B = Test._A; // undefined, no error/warning
|
||||
Test._A = 3;
|
||||
14
tests/cases/compiler/forwardRefInClassProperties.ts
Normal file
14
tests/cases/compiler/forwardRefInClassProperties.ts
Normal file
@ -0,0 +1,14 @@
|
||||
class Test
|
||||
{
|
||||
_b = this._a; // undefined, no error/warning
|
||||
_a = 3;
|
||||
|
||||
static _B = Test._A; // undefined, no error/warning
|
||||
static _A = 3;
|
||||
|
||||
method()
|
||||
{
|
||||
let a = b; // Block-scoped variable 'b' used before its declaration
|
||||
let b = 3;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user