Skip to content

Fixed a redundant used before defined error #55283

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 5 additions & 6 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//// [tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts] ////

=== noUsedBeforeDefinedErrorInTypeContext.ts ===
// https://github.com/microsoft/TypeScript/issues/8775

interface IThing<T> {
>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<typeof foo>,
>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<typeof bar>,
>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<typeof bar>,
>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<typeof qwe>,
>four : Symbol(four, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 18, 13))
>IThing : Symbol(IThing, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 0, 0))
>qwe : Symbol(qwe, Decl(noUsedBeforeDefinedErrorInTypeContext.ts, 18, 5))
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
//// [tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts] ////

=== noUsedBeforeDefinedErrorInTypeContext.ts ===
// https://github.com/microsoft/TypeScript/issues/8775

interface IThing<T> {
owner: T;
>owner : T
}

var foo = {
>foo : any
>{ one: {} as IThing<typeof foo>,} : { one: IThing<any>; }

one: {} as IThing<typeof foo>,
>one : IThing<any>
>{} as IThing<typeof foo> : IThing<any>
>{} : {}
>foo : any
}

let baz = {
>baz : { two: IThing<any>; }
>{ two: {} as IThing<typeof bar>,} : { two: IThing<any>; }

two: {} as IThing<typeof bar>,
>two : IThing<any>
>{} as IThing<typeof bar> : IThing<any>
>{} : {}
>bar : any
}

let bar = {
>bar : any
>{ three: {} as IThing<typeof bar>,} : { three: IThing<any>; }

three: {} as IThing<typeof bar>,
>three : IThing<any>
>{} as IThing<typeof bar> : IThing<any>
>{} : {}
>bar : any
}

const qwe = {
>qwe : any
>{ four: {} as IThing<typeof qwe>,} : { four: IThing<any>; }

four: {} as IThing<typeof qwe>,
>four : IThing<any>
>{} as IThing<typeof qwe> : IThing<any>
>{} : {}
>qwe : any
}

23 changes: 23 additions & 0 deletions tests/cases/compiler/noUsedBeforeDefinedErrorInTypeContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// @noEmit: true

// https://github.com/microsoft/TypeScript/issues/8775

interface IThing<T> {
owner: T;
}

var foo = {
one: {} as IThing<typeof foo>,
}

let baz = {
two: {} as IThing<typeof bar>,
}

let bar = {
three: {} as IThing<typeof bar>,
}
Comment on lines +17 to +19
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this isn't actually super useful as the result of this is an implicit any but that's a separate error category and the reported "no used before defined" was still redundant

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you set @strict: true to show that the no implicit any errors are there?


const qwe = {
four: {} as IThing<typeof qwe>,
}