-
Notifications
You must be signed in to change notification settings - Fork 12.9k
Skip ambient modules in globalThis #48938
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
Conversation
Previously, globalThis mistakenly included ambient modules, even though these are not values: ```ts declare module "ambientModule" { export type typ = 1 export var val: typ } type Oops = (typeof globalThis)[\"ambientModule\"] ``` This PR adds ambient modules to the kinds of things that are skipped when constructing `globalThis`' properties, along with block-scoped variables.
@@ -11507,7 +11507,7 @@ namespace ts { | |||
if (symbol === globalThisSymbol) { | |||
const varsOnly = new Map<string, Symbol>() as SymbolTable; | |||
members.forEach(p => { | |||
if (!(p.flags & SymbolFlags.BlockScoped)) { | |||
if (!(p.flags & SymbolFlags.BlockScoped) && !(p.flags & SymbolFlags.ValueModule && some(p.declarations, isAmbientModule))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some questions:
- what's this
ValueModule
symbol flag/what is a value module? - is it precise enough to check if some declaration is an ambient module? what if some of the other declarations are not ambient modules? (that question may be answered by the one above)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- A value module is a namespace OR
declare module "foo"
with values inside it. A namespace module is a namespace that has only types inside it. (Both kinds of module can contain other namespaces; any nested value turns the parent namespaces into value modules.) - I think the binder only allows ambient modules to merge with ambient modules? I'll have to check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm, the excludes are the same as other value modules: Value & ~(Function | Class | RegularEnum | ValueModule)
I'll see if I can name a function, class or enum "foo"
, but I don't think I can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, effectively only other ambient modules can merge with ambient modules.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll see if I can name a function, class or enum "foo", but I don't think I can.
You can if you use a js merge, eg,
globalThis['"module"'] = function () {}
or similar.
|
||
type GlobalThis = keyof typeof globalThis | ||
// should error | ||
const bad2: GlobalThis = 'ambientModule' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I understand what's being tested here, because the use of quotes makes it confusing for me.
const bad2: GlobalThis = 'ambientModule'
should error with or without this bug fix, no? Because the ambient module is "ambientModule"
, with the quotes. Or am I misunderstanding something?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the original test case, which was already confusing. I should probably just leave it out: it turns out that keyof
turns "\"foo\""
into just "foo"
. But it's not relevant to whether "\"foo\""
should be there in the first place.
@@ -11507,7 +11507,7 @@ namespace ts { | |||
if (symbol === globalThisSymbol) { | |||
const varsOnly = new Map<string, Symbol>() as SymbolTable; | |||
members.forEach(p => { | |||
if (!(p.flags & SymbolFlags.BlockScoped)) { | |||
if (!(p.flags & SymbolFlags.BlockScoped) && !(p.flags & SymbolFlags.ValueModule && some(p.declarations, isAmbientModule))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
some
? What if you make a global named "module"
that ?merges? with the ambient module named "module"
? (Is such a merge even possible?)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How do you make a global named "module"
?
Edit: Ah, you give an example above. I guess every
then?
The modules are required to have at least one declaration so that our treatment of `globalThis` stays the same, and `globalThis.globalThis.globalThis` remains legal.
Previously, globalThis mistakenly included ambient modules, even though these are not values:
This PR adds ambient modules to the kinds of things that are skipped when constructing
globalThis
' properties, along with block-scoped variables.Fixes #48147