I'm quite new to TypeScript
I'm trying to write a wrapper around making a post request that looks like
import fetch from 'node-fetch';
async function post(
address: string,
url: string,
body: any,
headers?: Record<string, string>,
otherParams?: Omit<RequestInit, 'body' | 'method' | 'headers'>
) {
const response = await fetch(
`${address}${url}`,
{
method: 'POST',
body: JSON.stringify(body),
headers: headers,
...otherParams
}
);
return await response.json()
}
The intent is to have this as a convenient and easy to use wrapper, so it doesn't show right away what otherParams
are accepted (because in most cases they won't be necessary), but if a developer wants to check and use them - they can.
The above compiles fine. But I realised that we don't really need headers
there, so we moved to something like this (just removing mentions of headers from above):
import fetch from 'node-fetch';
async function post(
address: string,
url: string,
body: any,
otherParams?: Omit<RequestInit, 'body' | 'method'>
) {
const response = await fetch(
`${address}${url}`,
{
method: 'POST',
body: JSON.stringify(body),
...otherParams
}
);
return await response.json()
}
This however fails compilation with the following error:
minimal-breaking-example.ts:12:9 - error TS2345: Argument of type '{ signal?: AbortSignal; window?: null; referrer?: string; headers?: HeadersInit; cache?: RequestCache; credentials?: RequestCredentials; ... 6 more ...; body: string; }' is not assignable to parameter of type 'RequestInit'.
Types of property 'headers' are incompatible.
Type 'HeadersInit' is not assignable to type 'import("<my local path>/node_modules/node-fetch/@types/index").HeadersInit'.
Type 'Headers' is not assignable to type 'HeadersInit'.
Type 'Headers' is missing the following properties from type 'Headers': entries, keys, values, raw, [Symbol.iterator]
12 {
~
13 method: 'POST',
~~~~~~~~~~~~~~~~~~~~~~~~~~~
...
15 ...otherParams
~~~~~~~~~~~~~~~~~~~~~~~~~~
16 }
~~~~~~~~~
Maybe someone has some ideas on what's wrong here?
So, while writing the question, I actually figured it out. I'm making a note here in case someone else stumbles upon this.
I noticed that when I remove the import from node-fetch
, everything works properly. I also noticed that it specifically for type Header
, it says it's missing properties: entries
, keys
, values
, raw
, [Symbol.iterator]
.
Turned out that the RequestInit
type expected by fetch
and the global RequestInit
, which I've been using to create the new type with Omit
, are actually two different types. fetch
redefines the global types extending them with a few properties. Interestingly, the properties added to RequestInit
are all optional, however the ones added to Headers
are not. The fetch.Header
type requires, above all the properties from the global Header
, that entries
, keys
, values
, raw
, [Symbol.iterator]
are present (node-fetch Headers vs fetch Headers).
The solution here is to use fetch.RequestInit
instead of RequestInit
, i.e. this works just fine:
import fetch from 'node-fetch';
import { RequestInit } from 'node-fetch';
async function post(
address: string,
url: string,
body: any,
otherParams?: Omit<RequestInit, 'body' | 'method'>
) {
const response = await fetch(
`${address}${url}`,
{
method: 'POST',
body: JSON.stringify(body),
...otherParams
}
);
return await response.json()
}