From cf53743bd696de5b3a3eff71a13b799c8e390ea7 Mon Sep 17 00:00:00 2001 From: Andy Date: Thu, 14 Sep 2017 07:59:53 -0700 Subject: [PATCH] In `isInPropertyInitializer`, don't bail out at a `PropertyAssignment` (#18449) --- src/compiler/checker.ts | 16 +++++++++++++++- ...reDeclaration_propertyAssignment.errors.txt | 11 +++++++++++ .../useBeforeDeclaration_propertyAssignment.js | 18 ++++++++++++++++++ .../useBeforeDeclaration_propertyAssignment.ts | 4 ++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 tests/baselines/reference/useBeforeDeclaration_propertyAssignment.errors.txt create mode 100644 tests/baselines/reference/useBeforeDeclaration_propertyAssignment.js create mode 100644 tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 5c6a8b59119..1fae08328b3 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14753,7 +14753,7 @@ namespace ts { return; } - if (findAncestor(node, node => node.kind === SyntaxKind.PropertyDeclaration ? true : isExpression(node) ? false : "quit") && + if (isInPropertyInitializer(node) && !isBlockScopedNameDeclaredBeforeUse(valueDeclaration, right) && !isPropertyDeclaredInAncestorClass(prop)) { error(right, Diagnostics.Block_scoped_variable_0_used_before_its_declaration, unescapeLeadingUnderscores(right.escapedText)); @@ -14766,6 +14766,20 @@ namespace ts { } } + function isInPropertyInitializer(node: Node): boolean { + return !!findAncestor(node, node => { + switch (node.kind) { + case SyntaxKind.PropertyDeclaration: + return true; + case SyntaxKind.PropertyAssignment: + // We might be in `a = { b: this.b }`, so keep looking. See `tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts`. + return false; + default: + return isPartOfExpression(node) ? false : "quit"; + } + }); + } + /** * It's possible that "prop.valueDeclaration" is a local declaration, but the property was also declared in a superclass. * In that case we won't consider it used before its declaration, because it gets its value from the superclass' declaration. diff --git a/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.errors.txt b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.errors.txt new file mode 100644 index 00000000000..f237fd07c80 --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.errors.txt @@ -0,0 +1,11 @@ +tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts(2,27): error TS2448: Block-scoped variable 'b' used before its declaration. + + +==== tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts (1 errors) ==== + export class C { + public a = { b: this.b }; + ~ +!!! error TS2448: Block-scoped variable 'b' used before its declaration. + private b = 0; + } + \ No newline at end of file diff --git a/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.js b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.js new file mode 100644 index 00000000000..e34604e0a4e --- /dev/null +++ b/tests/baselines/reference/useBeforeDeclaration_propertyAssignment.js @@ -0,0 +1,18 @@ +//// [useBeforeDeclaration_propertyAssignment.ts] +export class C { + public a = { b: this.b }; + private b = 0; +} + + +//// [useBeforeDeclaration_propertyAssignment.js] +"use strict"; +exports.__esModule = true; +var C = /** @class */ (function () { + function C() { + this.a = { b: this.b }; + this.b = 0; + } + return C; +}()); +exports.C = C; diff --git a/tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts b/tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts new file mode 100644 index 00000000000..bdd84473c57 --- /dev/null +++ b/tests/cases/compiler/useBeforeDeclaration_propertyAssignment.ts @@ -0,0 +1,4 @@ +export class C { + public a = { b: this.b }; + private b = 0; +}