Skip to content

Commit 5425b77

Browse files
authored
feat: fetch types (#997)
fixes #953
1 parent 643c957 commit 5425b77

7 files changed

+492
-0
lines changed

.editorconfig

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# https://editorconfig.org/
2+
3+
root = true
4+
5+
[*]
6+
indent_size = 2
7+
indent_style = space
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true

index.d.ts

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ import MockAgent from './types/mock-agent'
1111
import mockErrors from './types/mock-errors'
1212
import { request, pipeline, stream, connect, upgrade } from './types/api'
1313

14+
export * from './types/fetch'
15+
export * from './types/file'
16+
export * from './types/formdata'
17+
1418
export { Dispatcher, Pool, Client, buildConnector, errors, Agent, request, stream, pipeline, connect, upgrade, setGlobalDispatcher, getGlobalDispatcher, MockClient, MockPool, MockAgent, mockErrors }
1519
export default Undici
1620

test/types/fetch.test-d.ts

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
import { URL } from 'url'
2+
import { Blob } from 'buffer'
3+
import { expectType, expectError } from 'tsd'
4+
import {
5+
BodyInit,
6+
fetch,
7+
FormData,
8+
Headers,
9+
HeadersInit,
10+
Request,
11+
RequestCache,
12+
RequestCredentials,
13+
RequestDestination,
14+
RequestInit,
15+
RequestMode,
16+
RequestRedirect,
17+
Response,
18+
ResponseInit,
19+
ResponseType,
20+
} from '../..'
21+
22+
const requestInit: RequestInit = {}
23+
const responseInit: ResponseInit = { status: 200, statusText: 'OK' }
24+
25+
declare const request: Request
26+
declare const headers: Headers
27+
declare const response: Response
28+
29+
expectType<string | undefined>(requestInit.method)
30+
expectType<boolean | undefined>(requestInit.keepalive)
31+
expectType<HeadersInit | undefined>(requestInit.headers)
32+
expectType<BodyInit | undefined>(requestInit.body)
33+
expectType<RequestRedirect | undefined>(requestInit.redirect)
34+
expectType<string | undefined>(requestInit.integrity)
35+
expectType<AbortSignal | undefined>(requestInit.signal)
36+
37+
expectType<number | undefined>(responseInit.status)
38+
expectType<string | undefined>(responseInit.statusText)
39+
expectType<HeadersInit | undefined>(responseInit.headers)
40+
41+
expectType<Headers>(new Headers())
42+
expectType<Headers>(new Headers({}))
43+
expectType<Headers>(new Headers([]))
44+
expectType<Headers>(new Headers(headers))
45+
expectType<Headers>(new Headers(undefined))
46+
47+
expectType<Request>(new Request(request))
48+
expectType<Request>(new Request('https://example.com'))
49+
expectType<Request>(new Request(new URL('https://example.com')))
50+
expectType<Request>(new Request(request, requestInit))
51+
expectType<Request>(new Request('https://example.com', requestInit))
52+
expectType<Request>(new Request(new URL('https://example.com'), requestInit))
53+
54+
expectType<Promise<Response>>(fetch(request))
55+
expectType<Promise<Response>>(fetch('https://example.com'))
56+
expectType<Promise<Response>>(fetch(new URL('https://example.com')))
57+
expectType<Promise<Response>>(fetch(request, requestInit))
58+
expectType<Promise<Response>>(fetch('https://example.com', requestInit))
59+
expectType<Promise<Response>>(fetch(new URL('https://example.com'), requestInit))
60+
61+
expectType<Response>(new Response())
62+
expectType<Response>(new Response(null))
63+
expectType<Response>(new Response('string'))
64+
expectType<Response>(new Response(new Blob([])))
65+
expectType<Response>(new Response(new FormData()))
66+
expectType<Response>(new Response(new Int8Array()))
67+
expectType<Response>(new Response(new Uint8Array()))
68+
expectType<Response>(new Response(new Uint8ClampedArray()))
69+
expectType<Response>(new Response(new Int16Array()))
70+
expectType<Response>(new Response(new Uint16Array()))
71+
expectType<Response>(new Response(new Int32Array()))
72+
expectType<Response>(new Response(new Uint32Array()))
73+
expectType<Response>(new Response(new Float32Array()))
74+
expectType<Response>(new Response(new Float64Array()))
75+
expectType<Response>(new Response(new BigInt64Array()))
76+
expectType<Response>(new Response(new BigUint64Array()))
77+
expectType<Response>(new Response(new ArrayBuffer(0)))
78+
expectType<Response>(new Response(null, responseInit))
79+
expectType<Response>(new Response('string', responseInit))
80+
expectType<Response>(new Response(new Blob([]), responseInit))
81+
expectType<Response>(new Response(new FormData(), responseInit))
82+
expectType<Response>(new Response(new Int8Array(), responseInit))
83+
expectType<Response>(new Response(new Uint8Array(), responseInit))
84+
expectType<Response>(new Response(new Uint8ClampedArray(), responseInit))
85+
expectType<Response>(new Response(new Int16Array(), responseInit))
86+
expectType<Response>(new Response(new Uint16Array(), responseInit))
87+
expectType<Response>(new Response(new Int32Array(), responseInit))
88+
expectType<Response>(new Response(new Uint32Array(), responseInit))
89+
expectType<Response>(new Response(new Float32Array(), responseInit))
90+
expectType<Response>(new Response(new Float64Array(), responseInit))
91+
expectType<Response>(new Response(new BigInt64Array(), responseInit))
92+
expectType<Response>(new Response(new BigUint64Array(), responseInit))
93+
expectType<Response>(new Response(new ArrayBuffer(0), responseInit))
94+
expectType<Response>(Response.error())
95+
expectType<Response>(Response.redirect('https://example.com', 301))
96+
expectType<Response>(Response.redirect('https://example.com', 302))
97+
expectType<Response>(Response.redirect('https://example.com', 303))
98+
expectType<Response>(Response.redirect('https://example.com', 307))
99+
expectType<Response>(Response.redirect('https://example.com', 308))
100+
expectError(Response.redirect('https://example.com', NaN))
101+
102+
expectType<void>(headers.append('key', 'value'))
103+
expectType<void>(headers.delete('key'))
104+
expectType<string | null>(headers.get('key'))
105+
expectType<boolean>(headers.has('key'))
106+
expectType<void>(headers.set('key', 'value'))
107+
expectType<IterableIterator<string>>(headers.keys())
108+
expectType<IterableIterator<string>>(headers.values())
109+
expectType<IterableIterator<[string, string]>>(headers.entries())
110+
111+
expectType<RequestCache>(request.cache)
112+
expectType<RequestCredentials>(request.credentials)
113+
expectType<RequestDestination>(request.destination)
114+
expectType<Headers>(request.headers)
115+
expectType<string>(request.integrity)
116+
expectType<string>(request.method)
117+
expectType<RequestMode>(request.mode)
118+
expectType<RequestRedirect>(request.redirect)
119+
expectType<string>(request.referrerPolicy)
120+
expectType<string>(request.url)
121+
expectType<boolean>(request.keepalive)
122+
expectType<AbortSignal>(request.signal)
123+
expectType<boolean>(request.bodyUsed)
124+
expectType<Promise<Buffer>>(request.arrayBuffer())
125+
expectType<Promise<Blob>>(request.blob())
126+
expectType<Promise<FormData>>(request.formData())
127+
expectType<Promise<unknown>>(request.json())
128+
expectType<Promise<string>>(request.text())
129+
expectType<Request>(request.clone())
130+
131+
expectType<Headers>(response.headers)
132+
expectType<boolean>(response.ok)
133+
expectType<number>(response.status)
134+
expectType<string>(response.statusText)
135+
expectType<ResponseType>(response.type)
136+
expectType<string>(response.url)
137+
expectType<boolean>(response.redirected)
138+
expectType<boolean>(response.bodyUsed)
139+
expectType<Promise<Buffer>>(response.arrayBuffer())
140+
expectType<Promise<Blob>>(response.blob())
141+
expectType<Promise<FormData>>(response.formData())
142+
expectType<Promise<unknown>>(response.json())
143+
expectType<Promise<string>>(response.text())
144+
expectType<Response>(response.clone())

test/types/formdata.test-d.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Blob } from 'buffer'
2+
import { expectAssignable, expectType } from 'tsd'
3+
import { File, FormData } from "../.."
4+
5+
declare const blob: Blob
6+
const formData = new FormData()
7+
expectType<FormData>(formData)
8+
9+
expectType<void>(formData.append('key', 'value'))
10+
expectType<void>(formData.append('key', blob))
11+
expectType<void>(formData.set('key', 'value'))
12+
expectType<void>(formData.set('key', blob))
13+
expectType<File | string | null>(formData.get('key'))
14+
expectType<File | string | null>(formData.get('key'))
15+
expectType<Array<File | string>>(formData.getAll('key'))
16+
expectType<Array<File | string>>(formData.getAll('key'))
17+
expectType<boolean>(formData.has('key'))
18+
expectType<void>(formData.delete('key'))
19+
expectAssignable<IterableIterator<string>>(formData.keys())
20+
expectAssignable<IterableIterator<File | string>>(formData.values())
21+
expectAssignable<IterableIterator<[string, File | string]>>(formData.entries())
22+
expectAssignable<IterableIterator<[string, File | string]>>(formData[Symbol.iterator]())

types/fetch.d.ts

+178
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
// based on https://github.com/Ethan-Arrowood/undici-fetch/blob/249269714db874351589d2d364a0645d5160ae71/index.d.ts (MIT license)
2+
// and https://github.com/node-fetch/node-fetch/blob/914ce6be5ec67a8bab63d68510aabf07cb818b6d/index.d.ts (MIT license)
3+
/// <reference types="node" />
4+
5+
import { Blob } from 'buffer'
6+
import { URL, URLSearchParams } from 'url'
7+
import { FormData } from './formdata'
8+
9+
export type RequestInfo = string | URL | Request
10+
11+
export declare function fetch (
12+
input: RequestInfo,
13+
init?: RequestInit
14+
): Promise<Response>
15+
16+
declare class ControlledAsyncIterable implements AsyncIterable<Uint8Array> {
17+
constructor (input: AsyncIterable<Uint8Array> | Iterable<Uint8Array>)
18+
data: AsyncIterable<Uint8Array>
19+
disturbed: boolean
20+
readonly [Symbol.asyncIterator]: () => AsyncIterator<Uint8Array>
21+
}
22+
23+
export type BodyInit =
24+
| ArrayBuffer
25+
| AsyncIterable<Uint8Array>
26+
| Blob
27+
| FormData
28+
| Iterable<Uint8Array>
29+
| NodeJS.ArrayBufferView
30+
| URLSearchParams
31+
| null
32+
| string
33+
34+
export interface BodyMixin {
35+
readonly body: ControlledAsyncIterable | null
36+
readonly bodyUsed: boolean
37+
38+
readonly arrayBuffer: () => Promise<Buffer>
39+
readonly blob: () => Promise<Blob>
40+
readonly formData: () => Promise<FormData>
41+
readonly json: () => Promise<unknown>
42+
readonly text: () => Promise<string>
43+
}
44+
45+
export type HeadersInit = Iterable<[string, string]> | Record<string, string>
46+
47+
export declare class Headers implements Iterable<[string, string]> {
48+
constructor (init?: HeadersInit)
49+
readonly append: (name: string, value: string) => void
50+
readonly delete: (name: string) => void
51+
readonly get: (name: string) => string | null
52+
readonly has: (name: string) => boolean
53+
readonly set: (name: string, value: string) => void
54+
readonly forEach: (
55+
callbackfn: (value: string, key: string, iterable: Headers) => void,
56+
thisArg?: unknown
57+
) => void
58+
59+
readonly keys: () => IterableIterator<string>
60+
readonly values: () => IterableIterator<string>
61+
readonly entries: () => IterableIterator<[string, string]>
62+
readonly [Symbol.iterator]: () => Iterator<[string, string]>
63+
}
64+
65+
export type RequestCache =
66+
| 'default'
67+
| 'force-cache'
68+
| 'no-cache'
69+
| 'no-store'
70+
| 'only-if-cached'
71+
| 'reload'
72+
73+
export type RequestCredentials = 'omit' | 'include' | 'same-origin'
74+
75+
type RequestDestination =
76+
| ''
77+
| 'audio'
78+
| 'audioworklet'
79+
| 'document'
80+
| 'embed'
81+
| 'font'
82+
| 'image'
83+
| 'manifest'
84+
| 'object'
85+
| 'paintworklet'
86+
| 'report'
87+
| 'script'
88+
| 'sharedworker'
89+
| 'style'
90+
| 'track'
91+
| 'video'
92+
| 'worker'
93+
| 'xslt'
94+
95+
export interface RequestInit {
96+
readonly method?: string
97+
readonly keepalive?: boolean
98+
readonly headers?: HeadersInit
99+
readonly body?: BodyInit
100+
readonly redirect?: RequestRedirect
101+
readonly integrity?: string
102+
readonly signal?: AbortSignal
103+
}
104+
105+
export type RequestMode = 'cors' | 'navigate' | 'no-cors' | 'same-origin'
106+
107+
export type RequestRedirect = 'error' | 'follow' | 'manual'
108+
109+
export declare class Request implements BodyMixin {
110+
constructor (input: RequestInfo, init?: RequestInit)
111+
112+
readonly cache: RequestCache
113+
readonly credentials: RequestCredentials
114+
readonly destination: RequestDestination
115+
readonly headers: Headers
116+
readonly integrity: string
117+
readonly method: string
118+
readonly mode: RequestMode
119+
readonly redirect: RequestRedirect
120+
readonly referrerPolicy: string
121+
readonly url: string
122+
123+
readonly keepalive: boolean
124+
readonly signal: AbortSignal
125+
126+
readonly body: ControlledAsyncIterable | null
127+
readonly bodyUsed: boolean
128+
129+
readonly arrayBuffer: () => Promise<Buffer>
130+
readonly blob: () => Promise<Blob>
131+
readonly formData: () => Promise<FormData>
132+
readonly json: () => Promise<unknown>
133+
readonly text: () => Promise<string>
134+
135+
readonly clone: () => Request
136+
}
137+
138+
export interface ResponseInit {
139+
readonly status?: number
140+
readonly statusText?: string
141+
readonly headers?: HeadersInit
142+
}
143+
144+
export type ResponseType =
145+
| 'basic'
146+
| 'cors'
147+
| 'default'
148+
| 'error'
149+
| 'opaque'
150+
| 'opaqueredirect'
151+
152+
export type ResponseRedirectStatus = 301 | 302 | 303 | 307 | 308
153+
154+
export declare class Response implements BodyMixin {
155+
constructor (body?: BodyInit, init?: ResponseInit)
156+
157+
readonly headers: Headers
158+
readonly ok: boolean
159+
readonly status: number
160+
readonly statusText: string
161+
readonly type: ResponseType
162+
readonly url: string
163+
readonly redirected: boolean
164+
165+
readonly body: ControlledAsyncIterable | null
166+
readonly bodyUsed: boolean
167+
168+
readonly arrayBuffer: () => Promise<Buffer>
169+
readonly blob: () => Promise<Blob>
170+
readonly formData: () => Promise<FormData>
171+
readonly json: () => Promise<unknown>
172+
readonly text: () => Promise<string>
173+
174+
readonly clone: () => Response
175+
176+
static error (): Response
177+
static redirect (url: string | URL, status: ResponseRedirectStatus): Response
178+
}

0 commit comments

Comments
 (0)