ReactiveX / rxjs

A reactive programming library for JavaScript
https://rxjs.dev
Apache License 2.0
30.6k stars 3k forks source link

Suggestion: document how to use `fromFetch` in Node with TypeScript #6149

Open OliverJAsh opened 3 years ago

OliverJAsh commented 3 years ago

Suggestion

Here is a very simple Node project that attempts to use fromFetch: https://github.com/OliverJAsh/rxjs-fromfetch-node

import * as nodeFetch from 'node-fetch'
import { fromFetch } from 'rxjs/fetch';

declare global {
    var fetch: typeof nodeFetch.default;
}
global.fetch = nodeFetch.default;

fromFetch;

Running tsc returns a bunch of errors. For example:

$ tsc
node_modules/rxjs/dist/types/internal/observable/dom/fetch.d.ts:3:54 - error TS2304: Cannot find name 'Request'.

3 export declare function fromFetch<T>(input: string | Request, init: RequestInit & {
                                                       ~~~~~~~

node_modules/rxjs/dist/types/internal/observable/dom/fetch.d.ts:3:69 - error TS2304: Cannot find name 'RequestInit'.

3 export declare function fromFetch<T>(input: string | Request, init: RequestInit & {
                                                                      ~~~~~~~~~~~

node_modules/rxjs/dist/types/internal/observable/dom/fetch.d.ts:4:26 - error TS2304: Cannot find name 'Response'.

4     selector: (response: Response) => ObservableInput<T>;

If I understand correctly, this is because the RxJS typings are relying on globals provided by TypeScript's DOM lib, but these globals will not exist for a Node project because the DOM lib is intentionally omitted:

tsconfig.json:

        // We've intentionally omitted `DOM` since this is a Node project.
        "lib": ["ES2015"],

To workaround this issue I had to create the globals myself:

 import * as nodeFetch from 'node-fetch'
 import { fromFetch } from 'rxjs/fetch';

 declare global {
     var fetch: typeof nodeFetch.default;
+    type Request = nodeFetch.Request;
+    type RequestInit = nodeFetch.RequestInit;
+    type Response = nodeFetch.Response;
 }
 global.fetch = nodeFetch.default;

 fromFetch;

This issue isn't immediately obvious, so perhaps we should document the steps to get it working. Or perhaps there is a way to rewrite the RxJS types so they never rely on the DOM lib—after all, we shouldn't make assumptions about the environment the code will run in. What do you think?

cartant commented 3 years ago

We can probably do something similar to this internally so that those types are available with DOM being in lib:

https://github.com/ReactiveX/rxjs/blob/032f6815779bd7e3c0f2317748043b084bee01d3/src/internal/types.ts#L1-L2

OliverJAsh commented 3 years ago

That would work but it would be a shame if we had to bring in all of the DOM types. That would mean I could reference a global such as window in my Node project, which doesn't really make sense.

Ideally TypeScript would provide the types for fetch in a separate lib so we could just bundle that.

cartant commented 3 years ago

We'd only be able to do it if it was in a fetch-specific file, but even then - thinking about it more - I'm not sure we'd want to, as it'd always drag those types into the global namespace.

benlesh commented 3 years ago

The only other thing I can think of is that we introduce our own interface that mirrors what is available in both DOM and Node impls. Then we don't have to worry about it. The only problem there is it becomes easier to mess up what we're developing. As maybe we get a property name wrong or something. :\