Skip to content
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

a way to import from a module without including its globals #50424

Open
5 tasks done
DetachHead opened this issue Aug 24, 2022 · 2 comments
Open
5 tasks done

a way to import from a module without including its globals #50424

DetachHead opened this issue Aug 24, 2022 · 2 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@DetachHead
Copy link
Contributor

DetachHead commented Aug 24, 2022

Suggestion

πŸ” Search Terms

import without global types

βœ… Viability Checklist

My suggestion meets these guidelines:

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

it would be useful if there was a way to import from a module that has globally declared symbols, without importing those as well.

for example:

// node_modules/foo/main.d.ts
// i want to import some useful type from this 3rd party module, but it incorrectly assumes it will be run
// in an environment with `value` globally available
declare global {
    const value: string
    type GlobalFoo = {}
}
export type Foo = {}
// main.ts
// my module
import {Foo} from 'foo'
const foo: Foo = {}
const globalFoo: GlobalFoo = {} // no error
console.log(value) // no error

here, i only want to be able to use what i imported from the foo module. i don't want any of the globally declared stuff

i guess there are two parts to this feature request (or maybe only one of them is needed?):

1. a keyword on the import statement to prevent globals from being included in the import

import noglobals {Foo} from 'foo'

2. the ability to import globally declared types directly

if you want to be able to use them without importing all of the globals from the module into the global scope:

import type {GlobalFoo} from 'foo'

obviously, this should only be allowed for type imports, as it would otherwise fail at runtime

πŸ“ƒ Motivating Example

in playwright, the dom types are imported in order to support writing code that gets executed within the browser - see microsoft/playwright#3037. specifically, this is a workaround for #43434

however, there's no way to disable this without breaking everything - microsoft/playwright#16707

ideally, playwright should be able to import and use the dom types without incorrectly declaring globals like window to exist in the nodejs runtime

πŸ’» Use Cases

i think there are countless examples in the wild of modules that incorrectly leak browser globals into node and vice versa. here are some recent examples i've come across:

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature labels Aug 25, 2022
@mrmckeb
Copy link

mrmckeb commented Sep 1, 2022

I feel that there could also be a TypeScript setting (in tsconfig.json) to disallow global types completely.

Some reasons to prefer no-globals:

  • This ensures consistency. For example, should I import React - or just use the global React namespace?
  • This fits in better with modern JavaScript now that we have ESM. Libraries like @playwright/test and vitest are also preferencing imports over globals for things like test.

But most importantly, when libraries modify global types, this can result in very hard to understand/resolve issues where TypeScript tells you that two types that you have limited control over are not compatible.

Some examples of packages that modify globals that we've had trouble with recently:

Let me know if this should be a separate issue and I'll create that.

@robbiespeed
Copy link

robbiespeed commented Jun 21, 2023

Option 2 seems kinda hard to implement without some extra syntax since there would be no way to distinguish between a explicitly exported type or a type from the global. Ex:

// types.d.ts
declare global {
  type Foo = 'foo';
}
export type Global = { bar: 'bar' };
export type Foo = 'baz';

// main.ts
import type { Foo, Global } from "types";

What is the type of Foo and Global?

// Import non exported globals without polluting projects globals 
import type globals { Foo } from "types";
// Foo is 'foo'

// Importing regular exported types still works, but no longer pollutes projects globals
import type { Foo, Global } from "types";
// Foo is 'baz'; Global is { bar: 'bar' };

A feature like this would help immensely for authoring platform agnostic libs:

import type globals { Window } from '@types/web';
import type globals { global } from '@types/node';

declare const globalThis: Partial<Window> & Partial<global>;

const scheduler = globalThis.requestIdleCallback ?? globalThis.queueMicrotask ?? (cb) => Promise.resolve().then(cb);

So much nicer than having to manually make shallow copies of the global interfaces, which can be quite error prone.

Option 1 doesn't really seem necessary so long as there is a new flag (--isolatedTypeImports?) to disallow pollution of globals for type only imports. I don't really see any use case for wanting a type only import to pollute globals, but it would be a breaking change so the flag would probably have to default to false.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

4 participants