From 6a132dde62ec6261a4a68bfb26efd85dc0bae699 Mon Sep 17 00:00:00 2001 From: Evgeniy Timokhov Date: Sat, 30 Dec 2023 08:44:36 +0000 Subject: [PATCH] Fixed finding a scope for compiler-level defined symbols like `globalThis` or `undefined` Fixes #282 --- src/collisions-resolver.ts | 18 +++++++++++++++--- tests/e2e/test-cases/globalThis/config.ts | 5 +++++ tests/e2e/test-cases/globalThis/index.spec.js | 1 + tests/e2e/test-cases/globalThis/input.ts | 2 ++ tests/e2e/test-cases/globalThis/output.d.ts | 4 ++++ 5 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 tests/e2e/test-cases/globalThis/config.ts create mode 100644 tests/e2e/test-cases/globalThis/index.spec.js create mode 100644 tests/e2e/test-cases/globalThis/input.ts create mode 100644 tests/e2e/test-cases/globalThis/output.d.ts diff --git a/src/collisions-resolver.ts b/src/collisions-resolver.ts index b412c5f..5f9b6c0 100644 --- a/src/collisions-resolver.ts +++ b/src/collisions-resolver.ts @@ -78,9 +78,7 @@ export class CollisionsResolver { return null; } - // we assume that all symbols for a given identifier will be in the same scope (i.e. defined in the same namespaces-chain) - // so we can use any declaration to find that scope as they all will have the same scope - const symbolScopePath = this.getNodeScope(getDeclarationsForSymbol(identifierSymbol)[0]); + const symbolScopePath = this.getSymbolScope(identifierSymbol); // this scope defines where the current identifier is located const currentIdentifierScope = this.getNodeScope(referencedIdentifier); @@ -184,6 +182,20 @@ export class CollisionsResolver { return identifierParts.join('.'); } + private getSymbolScope(identifierSymbol: ts.Symbol): ts.Symbol[] { + const identifierDeclarations = getDeclarationsForSymbol(identifierSymbol); + + // not all symbols have declarations, e.g. `globalThis` or `undefined` (not type but value e.g. in `typeof undefined`) + // they are "fake" symbols that exist only at the compiler level (see checker.ts file in in the compiler or `globals.set()` calls) + if (identifierDeclarations.length === 0) { + return []; + } + + // we assume that all symbols for a given identifier will be in the same scope (i.e. defined in the same namespaces-chain) + // so we can use any declaration to find that scope as they all will have the same scope + return this.getNodeScope(identifierDeclarations[0]); + } + /** * Returns a node's scope where it is located in terms of namespaces/modules. * E.g. A scope for `Opt` in `declare module foo { type Opt = number; }` is `[Symbol(foo)]` diff --git a/tests/e2e/test-cases/globalThis/config.ts b/tests/e2e/test-cases/globalThis/config.ts new file mode 100644 index 0000000..a1953e7 --- /dev/null +++ b/tests/e2e/test-cases/globalThis/config.ts @@ -0,0 +1,5 @@ +import { TestCaseConfig } from '../test-case-config'; + +const config: TestCaseConfig = {}; + +export = config; diff --git a/tests/e2e/test-cases/globalThis/index.spec.js b/tests/e2e/test-cases/globalThis/index.spec.js new file mode 100644 index 0000000..c015c26 --- /dev/null +++ b/tests/e2e/test-cases/globalThis/index.spec.js @@ -0,0 +1 @@ +require('../run-test-case').runTestCase(__dirname); diff --git a/tests/e2e/test-cases/globalThis/input.ts b/tests/e2e/test-cases/globalThis/input.ts new file mode 100644 index 0000000..043ad2a --- /dev/null +++ b/tests/e2e/test-cases/globalThis/input.ts @@ -0,0 +1,2 @@ +export declare function getGlobal(): typeof globalThis; +export declare function getUndef(): typeof undefined; diff --git a/tests/e2e/test-cases/globalThis/output.d.ts b/tests/e2e/test-cases/globalThis/output.d.ts new file mode 100644 index 0000000..b26352d --- /dev/null +++ b/tests/e2e/test-cases/globalThis/output.d.ts @@ -0,0 +1,4 @@ +export declare function getGlobal(): typeof globalThis; +export declare function getUndef(): typeof undefined; + +export {};