From ef9b771abccba8ba5f802895f04817728ff9f2c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Burzy=C5=84ski?= Date: Sun, 6 Aug 2023 11:18:00 +0200 Subject: [PATCH] Fixed a redundant used before defined error --- src/compiler/checker.ts | 11 ++-- ...sedBeforeDefinedErrorInTypeContext.symbols | 50 +++++++++++++++++ ...oUsedBeforeDefinedErrorInTypeContext.types | 54 +++++++++++++++++++ .../noUsedBeforeDefinedErrorInTypeContext.ts | 23 ++++++++ 4 files changed, 132 insertions(+), 6 deletions(-) create mode 100644 tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols create mode 100644 tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.types create mode 100644 tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 09c364049ad70..f1e2dc2e27b79 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2761,6 +2761,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return sourceFiles.indexOf(declarationFile) <= sourceFiles.indexOf(useFile); } + // deferred usage in a type context is always OK regardless of the usage position: + if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isInAmbientOrTypeNode(usage)) { + return true; + } + if (declaration.pos <= usage.pos && !(isPropertyDeclaration(declaration) && isThisProperty(usage.parent) && !declaration.initializer && !declaration.exclamationToken)) { // declaration is before usage if (declaration.kind === SyntaxKind.BindingElement) { @@ -2802,9 +2807,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // (except when emitStandardClassFields: true and the reference is to a parameter property) // 4. inside a static property initializer, a reference to a static method in the same class // 5. inside a TS export= declaration (since we will move the export statement during emit to avoid TDZ) - // or if usage is in a type context: - // 1. inside a type query (typeof in type position) - // 2. inside a jsdoc comment if (usage.parent.kind === SyntaxKind.ExportSpecifier || (usage.parent.kind === SyntaxKind.ExportAssignment && (usage.parent as ExportAssignment).isExportEquals)) { // export specifiers do not use the variable, they only make it available for use return true; @@ -2814,9 +2816,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return true; } - if (!!(usage.flags & NodeFlags.JSDoc) || isInTypeQuery(usage) || isInAmbientOrTypeNode(usage)) { - return true; - } if (isUsedInFunctionOrInstanceProperty(usage, declaration)) { if (emitStandardClassFields && getContainingClass(declaration) diff --git a/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols new file mode 100644 index 0000000000000..2ac6072a46bf3 --- /dev/null +++ b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols @@ -0,0 +1,50 @@ +//// [tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts] //// + +=== noUsedBeforeDefinedErrorInTypeContext.ts === +// https://github.com/microsoft/TypeScript/issues/8775 + +interface IThing { +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>T : Symbol(T, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 2, 17)) + + owner: T; +>owner : Symbol(IThing.owner, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 2, 21)) +>T : Symbol(T, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 2, 17)) +} + +var foo = { +>foo : Symbol(foo, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 6, 3)) + + one: {} as IThing, +>one : Symbol(one, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 6, 11)) +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>foo : Symbol(foo, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 6, 3)) +} + +let baz = { +>baz : Symbol(baz, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 10, 3)) + + two: {} as IThing, +>two : Symbol(two, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 10, 11)) +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>bar : Symbol(bar, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 14, 3)) +} + +let bar = { +>bar : Symbol(bar, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 14, 3)) + + three: {} as IThing, +>three : Symbol(three, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 14, 11)) +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>bar : Symbol(bar, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 14, 3)) +} + +const qwe = { +>qwe : Symbol(qwe, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 18, 5)) + + four: {} as IThing, +>four : Symbol(four, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 18, 13)) +>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0)) +>qwe : Symbol(qwe, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 18, 5)) +} + diff --git a/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.types b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.types new file mode 100644 index 0000000000000..247f743b916ef --- /dev/null +++ b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.types @@ -0,0 +1,54 @@ +//// [tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts] //// + +=== noUsedBeforeDefinedErrorInTypeContext.ts === +// https://github.com/microsoft/TypeScript/issues/8775 + +interface IThing { + owner: T; +>owner : T +} + +var foo = { +>foo : any +>{ one: {} as IThing,} : { one: IThing; } + + one: {} as IThing, +>one : IThing +>{} as IThing : IThing +>{} : {} +>foo : any +} + +let baz = { +>baz : { two: IThing; } +>{ two: {} as IThing,} : { two: IThing; } + + two: {} as IThing, +>two : IThing +>{} as IThing : IThing +>{} : {} +>bar : any +} + +let bar = { +>bar : any +>{ three: {} as IThing,} : { three: IThing; } + + three: {} as IThing, +>three : IThing +>{} as IThing : IThing +>{} : {} +>bar : any +} + +const qwe = { +>qwe : any +>{ four: {} as IThing,} : { four: IThing; } + + four: {} as IThing, +>four : IThing +>{} as IThing : IThing +>{} : {} +>qwe : any +} + diff --git a/tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts b/tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts new file mode 100644 index 0000000000000..7715278f189c0 --- /dev/null +++ b/tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts @@ -0,0 +1,23 @@ +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/8775 + +interface IThing { + owner: T; +} + +var foo = { + one: {} as IThing, +} + +let baz = { + two: {} as IThing, +} + +let bar = { + three: {} as IThing, +} + +const qwe = { + four: {} as IThing, +}