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

Support having '@types/web' as overriding 'dom' in TypeScript's lib resolver #44795

Open
4 tasks done
orta opened this issue Jun 28, 2021 · 13 comments
Open
4 tasks done
Labels
Suggestion An idea for TypeScript

Comments

@orta
Copy link
Contributor

orta commented Jun 28, 2021

Suggestion

I'd like to be able to install @types/web and have the following happen:

  • Other dependencies (like from DT) using something like /// <reference lib="dom"> do not resolve to the vendored libdom.d.ts
  • The default inclusion of dom in a project via lib would automatically pick up @types/web (or be fully suppressed)

🔍 Search Terms

dom types/web web lib

✅ 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
  • x ] 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

  1. Hardcode some sort of dom -> @types/web inside the TS compiler's
  2. Support some kind of pragma in @types/web

1 - Hardcoding

Pros:
@types/web is a special case (though there could be more in the future) and this could be the smallest possible change.

Cons:
It feels a bit meh.

2 - Pragmas

Pros:
We have existing pragmas: /// <reference no-default-lib="true"/> for type of thing. This answer is totally generic and would allow others to have their own implementations of other deployed libs.

Cons:
I'd need to check, but this might mean looking for the pragmas across all @types dts files before we can reliably use the files inlib

@andrewbranch andrewbranch added In Discussion Not yet reached consensus Suggestion An idea for TypeScript labels Jun 28, 2021
@MartinJohns
Copy link
Contributor

MartinJohns commented Jun 29, 2021

With the pragma approach, what would happen if two packages aim to replace the definitions? This could easily happen with unfortunate dependencies.

A third option may be to have it explicitly configured in the tsconfig.json. I'm the last person who'd ask for yet another flag / option, but it sounds sensible for this. This would allow replacement of any library file, and it's explicit (no magic).

@orta
Copy link
Contributor Author

orta commented Jun 29, 2021

Great thinking @MartinJohns

@sandersn was interested in having modules in lib for other reasons I think, I wonder if could be solved with the same concept:

{
  compilerOptions: {
    lib: ["es2015", { name: "dom", module: "@types/web" }]
  }
}

@MartinJohns
Copy link
Contributor

@sandersn was interested in having modules in lib for other reasons I think

What are the reasons? You didn't fill out the "Motivating Example" and "Use Cases" sections of the issue template. :-P

@orta
Copy link
Contributor Author

orta commented Jun 29, 2021

Chatted with wes (and I've marked this issue for design review tomorrow) but I'm mostly pretty convinced that a pragma is the right route here now that I understand that we basically read all the visible source-code before adding lib to a compile for type checking. So the cons for 2 don't apply.

I personally think if we hit a dupe we should throw, but realistically they would probably raise an error due to duplicated types anyway. Will see what happens at the language review meeting.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Jul 2, 2021

I might've missed some other context that was already discussed, but I don't think a pragma on its own is the right solution. Over time, .d.ts files getting published that say "no no no, let me tell you what to load" end up falling in conflict with what the end-user wants.

I personally think if we hit a dupe we should throw,

If we do end up shipping another pragma that seems reasonable, but it means we have to think about how a user can put their foot down and say exactly what gets loaded. The benefit of that is that you can give an error message saying "these two libraries conflict in what they're asking for".

I think ultimately, TypeScript users need to be able to control the environment that gets checked, and tsconfig.json needs to allow controlling that.

@DanielRosenwasser
Copy link
Member

Another thing to consider: what is the right way to declare a dependency on @types/web if you're a library? peerDependency?

@orta
Copy link
Contributor Author

orta commented Aug 19, 2021

I think this syntax makes it very explicit what is going on:

{
  compilerOptions: {
    lib: ["es2015", { replace: "dom", with: "@types/web" }]
  }
}

However it brings its own problems, because there's a question of how should this act:

{
  compilerOptions: {
    lib: [{ replace: "dom", with: "@types/web" }]
  }
}

Should it act like: lib: [] or lib: undefined?

I think it should probably act like: lib: [] - but it'll need documenting somewhere.

@orta
Copy link
Contributor Author

orta commented Sep 17, 2021

#45771 shipped this into the TypeScript 4.5 - the answer was to leave dependency managers to handle the resolution

This answer to supporting your own versions of in-built libraries uses the node_modules resolution structure to let you define libraries. The path lookup looks like:

  • lib.dom.d.ts -> @typescript/dom
  • lib.dom.iterable.d.ts -> @typescript/dom/iterable
  • lib.es2015.symbol.wellknown.d.ts -> @typescript/es2015/symbol-wellknown

If you made a package which just sets up @typescript/es2015/symbol-wellknown.d.ts without @typescript/es2015/index.d.ts, then es2015 would still resolve to the TypeScript hosted version.

All JS dependency managers support this syntax:

{
  "dependencies": {
    "@typescript/dom": "npm:@types/web"
  }
}

@alfaproject
Copy link

@orta is there documentation for this? I'd like to have some DOM types like top level URLSearchParams in node applications but I don't want to add dom to libs. I'd like to only have whatever node version X supports.

I'm wondering if this would be the way to do it or I'm looking at the wrong place

@orta
Copy link
Contributor Author

orta commented Feb 22, 2022

You'd need to add the types you want to your own .d.ts file which affects global scope, it's not related to this feature

@alfaproject
Copy link

We have the right node types and types: ["node"] which works for everything except for these shared browser globals, so I need to also add libs: ["dom"] to access these globals which are also available in node.

I thought this feature was going to enable to skip the libs: ["dom"] under these circumstances because I was under the impression that the node types don't have these globals because it conflicts with the bundled dom ones?

I'm sorry if this is really off-topic but I seem to be missing something obvious here o:

@orta
Copy link
Contributor Author

orta commented Feb 23, 2022

This feature lets you replace the lib files which ship with TypeScript in your projects. You could replace the dom lib with your custom subset but that's overcomplicating things when you can instead add the file to your project and not import the default dom at all.

@alfaproject
Copy link

So, no matter what, I need some form of dom instead of having the correct node types with all the available node globals? Alright, I guess. Seems a bit awkward but thanks for your guidance

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Suggestion An idea for TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants