-
Notifications
You must be signed in to change notification settings - Fork 226
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
Make types importable in Deno #254
base: master
Are you sure you want to change the base?
Conversation
I don't think this is strictly true, as we can append the import Protocol from 'devtools-protocol';
Protocol.Animation.AnimationType.CSSAnimation; Could you let me know how I can reproduce the problem you are experiencing and how I can verify that the problem is fixed? My hunch is that, rather than appending |
HI @TimvdLippe, yes the Typescript compiler does seem to allow a The example you provided happens to work in either Node or Deno, but here's one that currently only works in Node. It imports import { ProtocolMapping } from 'devtools-protocol/types/protocol-mapping';
function sendCommand<C extends keyof ProtocolMapping.Commands>(method: C, ...params: ProtocolMapping.Commands[C]["paramsType"]): Promise<ProtocolMapping.Commands[C]["returnType"]> {
return Promise.reject("todo");
}
sendCommand("Target.createTarget", { url: "https://bing.com" }); // Okay.
sendCommand("Target.createTarget", { foo: "bar" }); // Should fail type check. There's an example gist here that you can download and Here's a Deno version of the same example: import { ProtocolMapping } from "https://cdn.skypack.dev/[email protected]/types/protocol-mapping.d.ts";
function sendCommand<C extends keyof ProtocolMapping.Commands>(method: C, ...params: ProtocolMapping.Commands[C]["paramsType"]): Promise<ProtocolMapping.Commands[C]["returnType"]> {
return Promise.reject("todo");
}
sendCommand("Target.createTarget", { url: "https://bing.com" }); // Okay.
sendCommand("Target.createTarget", { foo: "bar" }); // Should fail type check. Deno's ESM module resolution requires extensions on all imports, so
We could fix this for Deno by importing |
Thought about this some more. A better idea might be to add the |
Yes that sounds like a better idea to me. If we can avoid file duplication, that would be ideal. That said, |
Yeah, to avoid circular dependencies and to avoid the use of import statements, all of |
commonJS vs. ESM is kind of a red herring because these aren't really ESM files, they're d.ts files. They have no emitted form and only exist at compile time. It would be nice if Deno allowed no file extension and/or tsc allowed It does seem like it would be good to avoid package.json
|
Thanks @brendankenny. Yeah, I guess this is not really about CommonJS/ESM but about incompatible ESM module resolution rules in different Typescript environments.
Yeah, disabling type checking wouldn't be ideal. Might as well not even bother using
I'm still learning about this stuff so it would be great if someone more familiar with import maps could weight in. From what I see though, they don't encourage using import maps to enable extension-less imports: https://github.com/WICG/import-maps#extension-less-imports. They do provide a straightforward example, but it's for remapping a bare module specifier like
This is slightly cumbersome if you're importing
This was the most elegant solution I could come up with. My sample file compiles and runs, and VS Code picks up types too. It's not too bad for a module like
Yes. This is meant to be used with JS file imports to point to separate TS declarations, but there's no JS file in our particular scenario, and I've confirmed that the same transitive import problem occurs. As of right now, our best options seem to be:
I'm still leaning towards (2). Aside from the pros mentioned earlier, exporting all three namespaces from a single entry point seems more idiomatic in an ESM world and might be a good idea regardless of the Deno/Node import issue. |
I've gone back over the issues and read the import maps explainer more closely. I've updated the PR to implement solution (2) above, exporting all namespace in a single file. It looks like TypeScript is unlikely to budge on their stance for making file extensions optional, based on their responses to the linked issue and others like it. Similarly, Deno seems set on requiring file extensions in import paths. While import maps can technically be used to "fix-up" problematic imports, this is not recommended and not the intended purpose of import maps which is to enable bare specifiers like Are there any concerns with the current solution? I'd be happy to update the readme as part of this PR. |
All right. I had another go at this and I think I understand the problem better now. I am not thrilled about copying all of our APIs into another file, that seems to not be great. That said, based on https://deno.land/[email protected]/typescript/types#using-x-typescript-types-header I think we can somewhat fix this by adding empty Sadly, I am not sure how I can test that it actually works. Do you know if adding the empty |
10c21b3
to
30bc2c4
Compare
defdf87
to
c9c207e
Compare
This change enables the
devtools-protocol
typings to be more easily imported in ESM-based environments like Deno.A new
esm
directory has been generated alongside the existingtypes
directory.esm
contains a copy of the Typescript definitions that play nice with ES module importing. CommonJS code or TypeScript code targeting Node.js will continue to work as-is and can import the definitions from the/types
directory. ESM code can import from/esm
instead.Motivation: A single file with no imports of its own like
protocol.d.ts
can already be imported in either an ESM or CommonJS environment without much trouble. The issue comes when trying to import a file that has further imports likeprotocol-mapping.d.ts
. This file imports theprotocol.d.ts
module but was written for one environment and won't work in the other. Specifically, ESM code requires a path to a file to be imported including the file extension (e.g../protocol.d.ts
). On the other hand TypeScript code targeting a Node.js environment requires no file extension (e.g../protocol
). The current state of TS/JS tooling seems to require having two different files to support both kinds of environment. More info at https://www.sensedeep.com/blog/posts/2021/how-to-create-single-source-npm-module.html.