Skip to content

Commit

Permalink
Allow typing the body of a RequestError response (#2325)
Browse files Browse the repository at this point in the history
  • Loading branch information
katsanva authored Jan 29, 2024
1 parent 3822412 commit 5e4f6ff
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 8 deletions.
9 changes: 5 additions & 4 deletions source/core/errors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ function isRequest(x: unknown): x is Request {
An error to be thrown when a request fails.
Contains a `code` property with error class code, like `ECONNREFUSED`.
*/
export class RequestError extends Error {
export class RequestError<T = unknown> extends Error {
input?: string;

code: string;
override stack!: string;
declare readonly options: Options;
readonly response?: Response;
readonly response?: Response<T>;
readonly request?: Request;
readonly timings?: Timings;

Expand Down Expand Up @@ -88,9 +88,10 @@ export class MaxRedirectsError extends RequestError {
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.
Includes a `response` property.
*/
// TODO: Change `HTTPError<T = any>` to `HTTPError<T = unknown>` in the next major version to enforce type usage.
// eslint-disable-next-line @typescript-eslint/naming-convention
export class HTTPError extends RequestError {
declare readonly response: Response;
export class HTTPError<T = any> extends RequestError<T> {
declare readonly response: Response<T>;
declare readonly request: Request;
declare readonly timings: Timings;

Expand Down
1 change: 1 addition & 0 deletions source/core/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -701,6 +701,7 @@ export default class Request extends Duplex implements RequestEvents<Request> {
let promises: Array<Promise<unknown>> = rawCookies.map(async (rawCookie: string) => (options.cookieJar as PromiseCookieJar).setCookie(rawCookie, url!.toString()));

if (options.ignoreInvalidCookies) {
// eslint-disable-next-line @typescript-eslint/no-floating-promises
promises = promises.map(async promise => {
try {
await promise;
Expand Down
29 changes: 25 additions & 4 deletions test/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ test('properties', withServer, async (t, server, got) => {

const url = new URL(server.url);

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

test('catches dns errors', async t => {
const error = (await t.throwsAsync<RequestError>(got('http://doesntexist', {retry: {limit: 0}})))!;
const error = (await t.throwsAsync<RequestError<undefined>>(got('http://doesntexist', {retry: {limit: 0}})))!;
t.truthy(error);
t.regex(error.message, /ENOTFOUND|EAI_AGAIN/);
t.is((error.options.url as URL).host, 'doesntexist');
Expand Down Expand Up @@ -110,7 +111,27 @@ test('custom body', withServer, async (t, server, got) => {
message: 'Response code 404 (Not Found)',
});
t.is(error?.response.statusCode, 404);
t.is(error?.response.body, 'not');
// Typecheck for default `any` type
t.assert(error?.response.body === 'not');
});

test('custom json body', withServer, async (t, server, got) => {
server.get('/', (_request, response) => {
response.statusCode = 404;
response.header('content-type', 'application/json');
response.end(JSON.stringify({
message: 'not found',
}));
});

const error = await t.throwsAsync<HTTPError<{message: string}>>(got('', {responseType: 'json'}),
{
instanceOf: HTTPError,
message: 'Response code 404 (Not Found)',
});
t.is(error?.response.statusCode, 404);
// Assert is used for body typecheck
t.assert(error?.response.body.message === 'not found');
});

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

0 comments on commit 5e4f6ff

Please sign in to comment.