diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 8e69c07877f..fd479090c53 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -2773,6 +2773,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) { @@ -2813,9 +2818,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; @@ -2825,9 +2827,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 diff --git a/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols b/tests/baselines/reference/noUsedBeforeDefinedErrorInTypeContext.symbols new file mode 100644 index 00000000000..2ac6072a46b --- /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 00000000000..247f743b916 --- /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 00000000000..7715278f189 --- /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, +}