-
Notifications
You must be signed in to change notification settings - Fork 30.5k
[node] Add fetch types #66824
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
[node] Add fetch types #66824
Conversation
types/node/globals.d.ts
Outdated
| namespace undici { | ||
| type Request = typeof globalThis extends { onmessage: any } ? {} : import('undici-types').Request; | ||
| type Response = typeof globalThis extends { onmessage: any } ? {} : import('undici-types').Response; | ||
| type FormData = typeof globalThis extends { onmessage: any } ? {} : import('undici-types').FormData; | ||
| type Headers = typeof globalThis extends { onmessage: any } ? {} : import('undici-types').Headers; | ||
| } |
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.
Where to expose these and what to name them is eligible for bikeshedding.
| "<=4.8": { "*": ["ts4.8/*"] } | ||
| }, | ||
| "dependencies": { | ||
| "undici-types": "~5.25.1" |
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 is the first version of undici-types available, but it reflects a newer minor version than is actually included in Node.js so far. It sounded from nodejs/undici#2261 (comment) like we will be able to wait on a new Node.js version comes out containing undici 5.25 and bump the @types/node version accordingly if we want this dependency mapping to be perfectly accurate. cc @Ethan-Arrowood
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.
Yeah, I don't think there's much of an issue shipping this now and then getting synced up later. If there's an actual issue (some bug or whatever), then just tell me exactly which version you want me to publish and I'll manually go a publish the undici-types version.
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.
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.
Kinda concerned about the impact of this on users of @types/node I've already seen a ton of duplicate installations of the package with different versions, using a restrictive version range for undici is likely going to make this worse.
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.
Can you elaborate? This isn’t undici, just undici-types.
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.
All those patch versions are identical, minus tiny bugfixes for other issues.
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.
More curious if we should make this >=5.22.1 to allow users squash/consolidate their dependencies more easily, a lot of libraries currently depend on @types/node@* to avoid this already, some don't and it causes multiple instances of @types/node to be installed which sometimes have been conflicting.
I'm not sure if I'm overstating the issue of if this is simply not a concern.
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.
Yeah, mainly I assume this is a question of "what if there are multiple types packages in a project", where previously @types/node had no deps so could only "conflict" with itself in a compatible fashion, but adding a dep expands that risk (where restricting the version range increases that risk). I'm not sure that it will actually cause a problem, though.
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.
The downside to loosening the range is indicating to a Node.js 20.3.X user (for example) that they have access to undici features that they actually don’t. undici just recently added support for HTTP2, so using a >= range could have incorrectly led users of a Node.js that hadn’t adopted that undici version to try to access those features, had this PR happened earlier.
On the other hand, I think you’re just automatically screwed if you have two @types/node versions in the same program, even without dependencies.
| // fetch | ||
| { | ||
| // This tsconfig.json references lib.dom.d.t.s. The fetch | ||
| // types included in globals.d.ts are designed to be empty | ||
| // merges when lib.dom.d.ts is included. This test ensures | ||
| // the merge works, but the types observed are from lib.dom.d.ts. | ||
| fetch("https://example.com").then(response => { | ||
| response.arrayBuffer(); // $ExpectType Promise<ArrayBuffer> | ||
| response.blob(); // $ExpectType Promise<Blob> | ||
| response.formData(); // $ExpectType Promise<FormData> | ||
|
|
||
| // undici-types uses `Promise<unknown>` for `json()` | ||
| // This $ExpectType will change if tsconfig.json drops | ||
| // lib.dom.d.ts. | ||
| response.json(); // $ExpectType Promise<any> | ||
| response.text(); // $ExpectType Promise<string> | ||
| }); | ||
| const fd = new FormData(); | ||
| fd.append("foo", "bar"); | ||
| const headers = new Headers(); | ||
| headers.append("Accept", "application/json"); | ||
| fetch("https://example.com", { body: fd }); | ||
| } |
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.
Open to additional test content suggestions
|
It seems like |
Type reference directives don’t go through I have a workaround, but it’s really ugly and requires a tsconfig.json change that’s currently illegal according to dtslint: 30fe632. We need to add a higher priority |
|
The other thing we can do in the short term is not include fetch typings in |
30fe632 to
9d22fdd
Compare
3d1dc6c to
b6a3e0f
Compare
|
Inspecting the JavaScript source for this package found some properties that are not in the .d.ts files. formidable (unpkg)was missing the following properties:
as well as these 1 other properties...json |
|
@andrewbranch Thank you for submitting this PR! This is a live comment which I will keep updated. 4 packages in this PR
Code ReviewsBecause this is a widely-used package, a DT maintainer will need to review it before it can be merged. You can test the changes of this PR in the Playground. Status
All of the items on the list are green. To merge, you need to post a comment including the string "Ready to merge" to bring in your changes. Diagnostic Information: What the bot saw about this PR{
"type": "info",
"now": "-",
"pr_number": 66824,
"author": "andrewbranch",
"headCommitOid": "e942e8076f6ef1139d130e2f70516891eb2952b3",
"mergeBaseOid": "868275941a6a6687b783bdadd80b8caf0d89dc00",
"lastPushDate": "2023-09-25T19:21:26.000Z",
"lastActivityDate": "2023-10-09T17:32:11.000Z",
"mergeOfferDate": "2023-10-09T16:00:14.000Z",
"mergeRequestDate": "2023-10-09T17:31:40.000Z",
"mergeRequestUser": "andrewbranch",
"hasMergeConflict": false,
"isFirstContribution": false,
"tooManyFiles": false,
"hugeChange": false,
"popularityLevel": "Critical",
"pkgInfo": [
{
"name": "formidable",
"kind": "edit",
"files": [
{
"path": "types/formidable/formidable-tests.ts",
"kind": "test"
}
],
"owners": [
"Nemo157",
"martin-badin",
"devLana"
],
"addedOwners": [],
"deletedOwners": [],
"popularityLevel": "Popular"
},
{
"name": "hidefile",
"kind": "edit",
"files": [
{
"path": "types/hidefile/test/hidefile-tests.cjs.ts",
"kind": "test"
}
],
"owners": [
"nelsonni"
],
"addedOwners": [],
"deletedOwners": [],
"popularityLevel": "Well-liked by everyone"
},
{
"name": "levelup",
"kind": "edit",
"files": [
{
"path": "types/levelup/levelup-tests.ts",
"kind": "test"
}
],
"owners": [
"MeirionHughes",
"danwbyrne",
"carsonfarmer",
"istherepie"
],
"addedOwners": [],
"deletedOwners": [],
"popularityLevel": "Popular"
},
{
"name": "node",
"kind": "edit",
"files": [
{
"path": "types/node/globals.d.ts",
"kind": "definition"
},
{
"path": "types/node/package.json",
"kind": "package-meta-ok"
},
{
"path": "types/node/test/globals.ts",
"kind": "test"
},
{
"path": "types/node/ts4.8/globals.d.ts",
"kind": "definition"
},
{
"path": "types/node/ts4.8/test/globals.ts",
"kind": "test"
}
],
"owners": [
"Microsoft",
"DefinitelyTyped",
"jkomyno",
"alvis",
"r3nya",
"btoueg",
"smac89",
"touffy",
"DeividasBakanas",
"eyqs",
"Hannes-Magnusson-CK",
"hoo29",
"kjin",
"ajafff",
"islishude",
"mwiktorczyk",
"mohsen1",
"n-e",
"galkin",
"parambirs",
"eps1lon",
"ThomasdenH",
"WilcoBakker",
"wwwy3y3",
"samuela",
"kuehlein",
"bhongy",
"chyzwar",
"trivikr",
"yoursunny",
"qwelias",
"ExE-Boss",
"peterblazejewicz",
"addaleax",
"victorperin",
"ZYSzys",
"NodeJS",
"LinusU",
"wafuwafu13",
"mcollina",
"Semigradsky"
],
"addedOwners": [],
"deletedOwners": [],
"popularityLevel": "Critical"
}
],
"reviews": [
{
"type": "approved",
"reviewer": "jakebailey",
"date": "2023-10-09T15:59:25.000Z",
"isMaintainer": true
},
{
"type": "approved",
"reviewer": "nelsonni",
"date": "2023-10-09T04:50:22.000Z",
"isMaintainer": false
},
{
"type": "stale",
"reviewer": "SimonSchick",
"date": "2023-10-07T09:01:20.000Z",
"abbrOid": "afce591"
},
{
"type": "approved",
"reviewer": "mcollina",
"date": "2023-10-07T07:14:59.000Z",
"isMaintainer": false
},
{
"type": "stale",
"reviewer": "thw0rted",
"date": "2023-09-29T13:03:55.000Z",
"abbrOid": "b6a3e0f"
},
{
"type": "stale",
"reviewer": "Semigradsky",
"date": "2023-09-29T07:01:34.000Z",
"abbrOid": "b6a3e0f"
},
{
"type": "stale",
"reviewer": "carsonfarmer",
"date": "2023-09-29T00:32:17.000Z",
"abbrOid": "b6a3e0f"
},
{
"type": "stale",
"reviewer": "Ethan-Arrowood",
"date": "2023-09-25T19:50:11.000Z",
"abbrOid": "afce591"
}
],
"mainBotCommentID": 1740155365,
"ciResult": "pass"
} |
|
🔔 @Nemo157 @martin-badin @devLana @nelsonni @MeirionHughes @danwbyrne @carsonfarmer @istherepie @microsoft @DefinitelyTyped @jkomyno @alvis @r3nya @btoueg @smac89 @Touffy @DeividasBakanas @eyqs @Hannes-Magnusson-CK @hoo29 @kjin @ajafff @islishude @mwiktorczyk @mohsen1 @n-e @galkin @parambirs @eps1lon @ThomasdenH @WilcoBakker @wwwy3y3 @samuela @kuehlein @bhongy @chyzwar @trivikr @yoursunny @qwelias @ExE-Boss @peterblazejewicz @addaleax @victorperin @ZYSzys @nodejs @LinusU @wafuwafu13 @mcollina @Semigradsky — please review this PR in the next few days. Be sure to explicitly select |
|
@Ethan-Arrowood Thank you for reviewing this PR! The author has pushed new commits since your last review. Could you take another look and submit a fresh review? |
carsonfarmer
left a comment
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 was asked to review this, just doing a quick one. Not sure why levelup types are being touched here?
nelsonni
left a comment
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.
From the perspective of hidefile, this is just a reordering of the expected types in the test. LGTM.
|
@Semigradsky, @Ethan-Arrowood Thank you for reviewing this PR! The author has pushed new commits since your last review. Could you take another look and submit a fresh review? |
|
Hi @andrewbranch ! I do land here and I see that the types is quite recent. (2 hours only !) I'm trying do test things with globalThis.fetch = async (
input: URL | RequestInfo,
options: RequestInit | undefined,
) => {};does output: Type '(input: URL | RequestInfo, options: RequestInit | undefined) => Promise<Response>' is not assignable to type '{ (input: URL | RequestInfo, init?: RequestInit | undefined): Promise<Response>; (input: RequestInfo, init?: RequestInit | undefined): Promise<...>; }'.
Types of parameters 'input' and 'input' are incompatible.
Type 'RequestInfo' is not assignable to type 'URL | RequestInfo'.
Type 'Request' is not assignable to type 'URL | RequestInfo'.
Property 'referrer' is missing in type 'import("/home/jdeniau/code/metch-fock/node_modules/undici-types/fetch").Request' but required in type 'Request'.ts(2322)
lib.dom.d.ts(18715, 14): 'referrer' is declared here.By sniffing the .d.ts file, I found that you sometimes use globalThis.fetch = async (
input: URL | NodeJS.fetch.RequestInfo,
options: NodeJS.fetch.RequestInit | undefined,
) => {};But another issue there: If I roll back from |
|
How about globalThis.fetch = async (input, options) => {
}The types will be inferred from context. |
|
Indeed, it is inferred, but having the types written makes it really easier to navigate into the types while developing. Furthermore, I do call other methods, for example to extract the url of the function getInputUrl(input: Parameters<typeof globalThis.fetch>[0]) {witch is way less readable than function getInputUrl(input: RequestInfo | URL) {Pushing this a little forward, I found that function getInputUrl(
input: Parameters<typeof globalThis.fetch>[0],
): string {
if (typeof input === 'string') {
return input;
}
if (input instanceof URL) {
return input.toString();
}
return input.url;
}The last Is it a conflict between typescript's |
|
Yes. Users should avoid having @types/node and lib.dom.d.ts at the same time, but this PR jumped through many hoops to ensure that usage of |
|
OK, no problem, I will try to do this. Thanks for your time 👍 |
|
Sorry for this notifying a lot of people, I figured the MR would notify less people than the issue, but I can also open a new issue to ask more direct questions and provide more snippets. @andrewbranch if I want to assign the dispatcher by creating an Agent or MockAgent or call setGlobalDispatcher, should I be importing using undici-types directly? I know that this issue is primarily focused on fetch itself, but there are other aspects of undici which I use for testing and I was wondering if I just need to keep undici-types as an explicit devDependency, or if I should rely on the dependency that types/node is pulling it in, or some other solution? Prior to undici-types being created, I have been including undici as a dependency. Examples: |
|
You can’t import values from import { Agent } from "undici-types";
new Agent(); // this will crash in Node.jsSo you would want to continue to depend on undici, yes. |
|
Hey @andrewbranch! As mentioned in cloudflare/workerd#1298, it’s not your fault but this breaks anything which overrides the fetch global with an incompatible type if they’re doing so through I wanted to raise the idea here (discussed further in this comment) of using TypeScript 5’s "exports": {
"workerd": {
"node": {
"types": "./index.workerd.d.ts",
},
"types": "./noop.d.ts"
},
"types": "./index.d.ts"
}By default, this neutralises (We would also add keys for anything else in WinterCG’s Runtime Keys list, but AFAIK only I think this solution would be reasonable because it would remain backwards-compatible by default and require users to add As a bonus, this could also solve a lot of the concerns and hangups in microsoft/TypeScript#18588 w/r/t browser packages accidentally polluting their namespaces with I’m not asking to solve this immediately, but would the (BTW I don’t work for Cloudflare; just a concerned user :P) |
|
There’s kind of a fundamental problem with any approach that removes types from a program. While it’s true that sometimes users wind up with // user code running on Cloudflare
import { querySmartAI } from "openai-node";
const res = querySmartAI("what is the melting point of mushrooms (my grandma used to tell me)");
// node_modules/openai-node/index.d.ts
/// <reference types="node" />
export function querySmartAI(query: string): NodeJS.ReadableStream;If you override |
|
Thanks for explaining a bit more of the motivation there! I also liked your comment in the original issue, which really gave me pause to think. Perhaps I should just back up and ask what you think the idiomatic solution looks like? (You have much much more insight into what TypeScript think about this than I do 😅)
|
|
I honestly don’t know what the best way forward would be, but I think patching @types/node and having users replace it via package manager functionality sounds the most promising.
No, I think it would be correct to omit them. My point was only that (1) omitting all of @types/node is probably not correct, but (2) I don’t want @types/node to be responsible for knowing how much of Cloudflare’s worker API overlaps with Node.js. |
|
Can we please get an intended usage example for this? Simply adding |
|
There shouldn't be anything special; it "just works" for me in a project where I just did: $ npm init --yes
$ npm install -D typescript @types/node
$ npx tsc --init
$ echo "fetch()" > index.ts(And set |
|
This PR exposed
Some of these are very simple structurally but if they don't exist in the global namespace, any library that refers to them will break, sometimes in confusing ways. ETA: It looks like ResponseInit is the only one on my list that's an |
|
1000 * ❤️ I'm still having to directly depend on udici-types to be able to use these runtime values, which feels dirty. Is it necessary? import { Dispatcher, getGlobalDispatcher, setGlobalDispatcher, ProxyAgent } from "undici-types"; |
|
The |
|
This fails when building under an ESNext module. Requiring to use NodeNext. |
I am using ESNext module and it works well. Make sure you are using the latest version of |

Closes #60924
Undici has published their included types in a standalone a package
undici-typesspecifically for use here. The types are added as a dependency and exposed as globals via the conditional merging tricks @thw0rted came up with forBlobet al. They disappear in favor of the DOM-defined globals in the presence of lib.dom.d.ts, though the types there and in undici-types are nearly identical—most users will not notice a difference.Due to a limitation of DefinitelyTyped infrastructure, this cannot currently be added to the v18 typings 😕. @jakebailey, @sandersn and I are working on an infrastructure overhaul that fixes the issue, but I don’t think it’s worth holding this PR up for—our ETA is unknown still.
npm test <package to test>.If changing an existing definition: