elbywan / wretch

A tiny wrapper built around fetch with an intuitive syntax. :candy:
MIT License
4.83k stars 98 forks source link

TypeScript build errors, fetch-related types are missing #83

Closed jimmed closed 4 years ago

jimmed commented 4 years ago

Hi there! First of all, thanks for creating and maintaining a library that's a real joy to use.

I've used wretch successfully in several other TypeScript projects before, but in a relatively new project, I'm getting TypeScript errors on build.

TypeScript build log

I've cut down the log to remove repeated errors. If you need a complete log let me know.

$ tsc
node_modules/wretch/dist/resolver.d.ts:9:40 - error TS2304: Cannot find name 'Response'.
9 export declare type WretcherResponse = Response & {
                                         ~~~~~~~~

node_modules/wretch/dist/resolver.d.ts:19:21 - error TS2304: Cannot find name 'Blob'.
19     blob: <Result = Blob>(cb?: (type: Blob) => Result) => Promise<Result>;
                       ~~~~

node_modules/wretch/dist/resolver.d.ts:20:25 - error TS2304: Cannot find name 'FormData'.
20     formData: <Result = FormData>(cb?: (type: FormData) => Result) => Promise<Result>;
                           ~~~~~~~~

node_modules/wretch/dist/resolver.d.ts:24:45 - error TS2304: Cannot find name 'AbortController'.
24     setTimeout: (time: number, controller?: AbortController) => ResponseChain;
                                               ~~~~~~~~~~~~~~~

node_modules/wretch/dist/wretcher.d.ts:4:39 - error TS2304: Cannot find name 'RequestInit'.
4 export declare type WretcherOptions = RequestInit & {
                                        ~~~~~~~~~~~

What I tried

What I found

Based on the typedefs included in wretch, it looks as though the code expects the missing types to be declared globally. I can see that these types are declared in @types/node-fetch, which is installed.

I have no idea how to proceed at this point, and I can't see what I'm doing wrong. Any suggestions/ideas?

Project links

elbywan commented 4 years ago

Hey @jimmed,

First of all, thanks for creating and maintaining a library that's a real joy to use.

❤️

I have no idea how to proceed at this point, and I can't see what I'm doing wrong. Any suggestions/ideas?

Could you try adding the dom value to the compilerOptions.lib field in your tsconfig.json?

"lib": ["ES2019", "dom"]

Without dom, typescript will assume that fetch and (more generally) the browser-associated type definitions are not available in the target environment.

jimmed commented 4 years ago

@elbywan thanks for the rapid reply!

Adding dom to compilerOptions.lib solved the issue, but I feel like that's an iffy workaround, as the rest of the DOM lib isn't really available.

elbywan commented 4 years ago

but I feel like that's an iffy workaround, as the rest of the DOM lib isn't really available

Oh indeed I'm sorry I replied a bit too fast! Indeed for nodejs it is a bit sketchy to include the dom lib.

The issue could (unsure) be related to the fact that node-fetch maintainers made some breaking changes and now include scoped typescript definition files: https://github.com/node-fetch/node-fetch/pull/810

I'll look into a way to make things work better, thanks for reporting the issue!

elbywan commented 4 years ago

@jimmed

Allright! So I played with the code a bit, but unfortunately did not found an elegant way of dealing with the issue.

The easier solution IMO would be to:

  1. Install the following packages:
# the polyfills
npm i node-fetch form-data abort-controller
# and the associated types, when needed
npm i -D @types/node-fetch @types/form-data
  1. Add these lines somewhere in a .ts or declaration file:
import { Blob as B, Response as R, RequestInit as RI } from 'node-fetch'
import { AbortController as AC } from 'abort-controller'
import * as FD from 'form-data'

declare module 'wretch/dist/resolver' {
  type Blob = B
  type Response = R
  type AbortController = AC
  type FormData = FD
}

declare module 'wretch/dist/wretcher' {
  type AbortController = AC
  type RequestInit = RI
}

I'm aware this is not very pretty, but at least it should avoid leaking definitions to the global scope!


Full code

import { Blob as B, Response as R, RequestInit as RI } from 'node-fetch'
import { AbortController as AC } from 'abort-controller'
import * as FD from 'form-data'

declare module 'wretch/dist/resolver' {
  type Blob = B
  type Response = R
  type AbortController = AC
  type FormData = FD
}

declare module 'wretch/dist/wretcher' {
  type AbortController = AC
  type RequestInit = RI
}

import wretch from 'wretch'
import fetch from 'node-fetch'

const client = wretch().polyfills({ fetch })
client.url('https://jsonplaceholder.typicode.com/todos/1').get().json(console.log)

Compiled with

./node_modules/.bin/tsc

Output

{ userId: 1, id: 1, title: 'delectus aut autem', completed: false }

tsconfig.json

{
  "compilerOptions": {
    "outDir": "./dist",
    "lib": ["ES2019"],
    "module": "CommonJS",
    "target": "ES2019",
    "moduleResolution": "node",
    "esModuleInterop": true,
    "declaration": true
  }
}

package.json

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "@types/form-data": "^2.5.0",
    "@types/node-fetch": "^2.5.7",
    "typescript": "^3.9.6"
  },
  "dependencies": {
    "abort-controller": "^3.0.0",
    "form-data": "^3.0.0",
    "node-fetch": "^2.6.0",
    "wretch": "^1.7.2"
  }
}