Skip to content

Commit 5e4f6ff

Browse files
authoredJan 29, 2024
Allow typing the body of a RequestError response (#2325)
1 parent 3822412 commit 5e4f6ff

File tree

3 files changed

+31
-8
lines changed

3 files changed

+31
-8
lines changed
 

‎source/core/errors.ts

+5-4
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,13 @@ function isRequest(x: unknown): x is Request {
1616
An error to be thrown when a request fails.
1717
Contains a `code` property with error class code, like `ECONNREFUSED`.
1818
*/
19-
export class RequestError extends Error {
19+
export class RequestError<T = unknown> extends Error {
2020
input?: string;
2121

2222
code: string;
2323
override stack!: string;
2424
declare readonly options: Options;
25-
readonly response?: Response;
25+
readonly response?: Response<T>;
2626
readonly request?: Request;
2727
readonly timings?: Timings;
2828

@@ -88,9 +88,10 @@ export class MaxRedirectsError extends RequestError {
8888
An error to be thrown when the server response code is not 2xx nor 3xx if `options.followRedirect` is `true`, but always except for 304.
8989
Includes a `response` property.
9090
*/
91+
// TODO: Change `HTTPError<T = any>` to `HTTPError<T = unknown>` in the next major version to enforce type usage.
9192
// eslint-disable-next-line @typescript-eslint/naming-convention
92-
export class HTTPError extends RequestError {
93-
declare readonly response: Response;
93+
export class HTTPError<T = any> extends RequestError<T> {
94+
declare readonly response: Response<T>;
9495
declare readonly request: Request;
9596
declare readonly timings: Timings;
9697

‎source/core/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -701,6 +701,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
701701
let promises: Array<Promise<unknown>> = rawCookies.map(async (rawCookie: string) => (options.cookieJar as PromiseCookieJar).setCookie(rawCookie, url!.toString()));
702702

703703
if (options.ignoreInvalidCookies) {
704+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
704705
promises = promises.map(async promise => {
705706
try {
706707
await promise;

‎test/error.ts

+25-4
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ test('properties', withServer, async (t, server, got) => {
2020

2121
const url = new URL(server.url);
2222

23-
const error = (await t.throwsAsync<HTTPError>(got('')))!;
23+
const error = (await t.throwsAsync<HTTPError<string>>(got('')))!;
2424
t.truthy(error);
2525
t.truthy(error.response);
2626
t.truthy(error.options);
@@ -30,11 +30,12 @@ test('properties', withServer, async (t, server, got) => {
3030
t.is(error.message, 'Response code 404 (Not Found)');
3131
t.deepEqual(error.options.url, url);
3232
t.is(error.response.headers.connection, 'keep-alive');
33-
t.is(error.response.body, 'not');
33+
// Assert is used for type checking
34+
t.assert(error.response.body === 'not');
3435
});
3536

3637
test('catches dns errors', async t => {
37-
const error = (await t.throwsAsync<RequestError>(got('http://doesntexist', {retry: {limit: 0}})))!;
38+
const error = (await t.throwsAsync<RequestError<undefined>>(got('http://doesntexist', {retry: {limit: 0}})))!;
3839
t.truthy(error);
3940
t.regex(error.message, /ENOTFOUND|EAI_AGAIN/);
4041
t.is((error.options.url as URL).host, 'doesntexist');
@@ -110,7 +111,27 @@ test('custom body', withServer, async (t, server, got) => {
110111
message: 'Response code 404 (Not Found)',
111112
});
112113
t.is(error?.response.statusCode, 404);
113-
t.is(error?.response.body, 'not');
114+
// Typecheck for default `any` type
115+
t.assert(error?.response.body === 'not');
116+
});
117+
118+
test('custom json body', withServer, async (t, server, got) => {
119+
server.get('/', (_request, response) => {
120+
response.statusCode = 404;
121+
response.header('content-type', 'application/json');
122+
response.end(JSON.stringify({
123+
message: 'not found',
124+
}));
125+
});
126+
127+
const error = await t.throwsAsync<HTTPError<{message: string}>>(got('', {responseType: 'json'}),
128+
{
129+
instanceOf: HTTPError,
130+
message: 'Response code 404 (Not Found)',
131+
});
132+
t.is(error?.response.statusCode, 404);
133+
// Assert is used for body typecheck
134+
t.assert(error?.response.body.message === 'not found');
114135
});
115136

116137
test('contains Got options', withServer, async (t, server, got) => {

0 commit comments

Comments
 (0)
Please sign in to comment.