Skip to content

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

Merged
merged 2 commits into from
May 5, 2022
Merged

Conversation

sandersn
Copy link
Member

@sandersn sandersn commented May 3, 2022

Previously, globalThis mistakenly included ambient modules, even though these are not values:

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.

Fixes #48147

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.
@typescript-bot typescript-bot added Author: Team For Milestone Bug PRs that fix a bug with a specific milestone labels May 3, 2022
@sandersn sandersn requested review from gabritto and weswigham May 3, 2022 17:02
@@ -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))) {
Copy link
Member

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)

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member Author

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.

Copy link
Member

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'
Copy link
Member

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?

Copy link
Member Author

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))) {
Copy link
Member

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?)

Copy link
Member Author

@sandersn sandersn May 4, 2022

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.
@sandersn sandersn merged commit 46e8306 into main May 5, 2022
@sandersn sandersn deleted the skip-ambient-modules-in-globalThis branch May 5, 2022 16:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Author: Team For Milestone Bug PRs that fix a bug with a specific milestone
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ambient external module name incorrectly appears in keyof typeof globalThis
4 participants