Skip to content

Commit cff4c7a

Browse files
authoredJun 23, 2022
Add typed JSON response (#437)
1 parent f34fb1f commit cff4c7a

File tree

7 files changed

+51
-40
lines changed

7 files changed

+51
-40
lines changed
 

‎package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,8 @@
7171
"raw-body": "^2.5.1",
7272
"ts-node": "^10.6.0",
7373
"typescript": "~4.6.2",
74-
"xo": "^0.48.0"
74+
"xo": "^0.48.0",
75+
"expect-type": "^0.13.0"
7576
},
7677
"sideEffects": false,
7778
"xo": {

‎source/core/Ky.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {HTTPError} from '../errors/HTTPError.js';
22
import {TimeoutError} from '../errors/TimeoutError.js';
33
import type {Hooks} from '../types/hooks.js';
44
import type {Input, InternalOptions, NormalizedOptions, Options, SearchParamsInit} from '../types/options.js';
5-
import {ResponsePromise} from '../types/response.js';
5+
import {ResponsePromise} from '../types/ResponsePromise.js';
66
import {deepMerge, mergeHeaders} from '../utils/merge.js';
77
import {normalizeRequestMethod, normalizeRetryOptions} from '../utils/normalize.js';
88
import {delay, timeout, TimeoutOptions} from '../utils/time.js';

‎source/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ export {
4444
AfterResponseHook,
4545
} from './types/hooks.js';
4646

47-
export {ResponsePromise} from './types/response.js';
47+
export {ResponsePromise} from './types/ResponsePromise.js';
48+
export {KyResponse} from './types/response.js';
4849
export {HTTPError} from './errors/HTTPError.js';
4950
export {TimeoutError} from './errors/TimeoutError.js';

‎source/types/ResponsePromise.ts

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
Returns a `Response` object with `Body` methods added for convenience. So you can, for example, call `ky.get(input).json()` directly without having to await the `Response` first. When called like that, an appropriate `Accept` header will be set depending on the body method used. Unlike the `Body` methods of `window.Fetch`; these will throw an `HTTPError` if the response status is not in the range of `200...299`. Also, `.json()` will return an empty string if the response status is `204` instead of throwing a parse error due to an empty body.
3+
*/
4+
import {KyResponse} from './response.js';
5+
6+
export interface ResponsePromise extends Promise<KyResponse> {
7+
arrayBuffer: () => Promise<ArrayBuffer>;
8+
9+
blob: () => Promise<Blob>;
10+
11+
formData: () => Promise<FormData>;
12+
13+
// TODO: Use `json<T extends JSONValue>(): Promise<T>;` when it's fixed in TS.
14+
// See https://github.com/microsoft/TypeScript/issues/15300 and https://github.com/sindresorhus/ky/pull/80
15+
/**
16+
Get the response body as JSON.
17+
18+
@example
19+
```
20+
import ky from 'ky';
21+
22+
const json = await ky(…).json();
23+
```
24+
25+
@example
26+
```
27+
import ky from 'ky';
28+
29+
interface Result {
30+
value: number;
31+
}
32+
33+
const result = await ky(…).json<Result>();
34+
```
35+
*/
36+
json: <T = unknown>() => Promise<T>;
37+
38+
text: () => Promise<string>;
39+
}

‎source/types/ky.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {stop} from '../core/constants.js';
22
import type {Input, Options} from './options.js';
3-
import type {ResponsePromise} from './response.js';
3+
import type {ResponsePromise} from './ResponsePromise.js';
44

55
export interface KyInstance {
66
/**

‎source/types/response.ts

+1-35
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,3 @@
1-
/**
2-
Returns a `Response` object with `Body` methods added for convenience. So you can, for example, call `ky.get(input).json()` directly without having to await the `Response` first. When called like that, an appropriate `Accept` header will be set depending on the body method used. Unlike the `Body` methods of `window.Fetch`; these will throw an `HTTPError` if the response status is not in the range of `200...299`. Also, `.json()` will return an empty string if the response status is `204` instead of throwing a parse error due to an empty body.
3-
*/
4-
export interface ResponsePromise extends Promise<Response> {
5-
arrayBuffer: () => Promise<ArrayBuffer>;
6-
7-
blob: () => Promise<Blob>;
8-
9-
formData: () => Promise<FormData>;
10-
11-
// TODO: Use `json<T extends JSONValue>(): Promise<T>;` when it's fixed in TS.
12-
// See https://github.com/microsoft/TypeScript/issues/15300 and https://github.com/sindresorhus/ky/pull/80
13-
/**
14-
Get the response body as JSON.
15-
16-
@example
17-
```
18-
import ky from 'ky';
19-
20-
const json = await ky(…).json();
21-
```
22-
23-
@example
24-
```
25-
import ky from 'ky';
26-
27-
interface Result {
28-
value: number;
29-
}
30-
31-
const result = await ky(…).json<Result>();
32-
```
33-
*/
1+
export interface KyResponse extends Response {
342
json: <T = unknown>() => Promise<T>;
35-
36-
text: () => Promise<string>;
373
}

‎test/main.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {Buffer} from 'node:buffer';
22
import test from 'ava';
33
import delay from 'delay';
4+
import {expectTypeOf} from 'expect-type';
45
import ky, {TimeoutError} from '../source/index.js';
56
import {createHttpTestServer} from './helpers/create-http-test-server.js';
67
import {parseRawBody} from './helpers/parse-body.js';
@@ -662,7 +663,10 @@ test('parseJson option with response.json()', async t => {
662663
extra: 'extraValue',
663664
}),
664665
});
665-
const responseJson = await response.json();
666+
667+
const responseJson = await response.json<{hello: string; extra: string}>();
668+
669+
expectTypeOf(responseJson).toMatchTypeOf({hello: 'world', extra: 'extraValue'});
666670

667671
t.deepEqual(responseJson, {
668672
...json,

0 commit comments

Comments
 (0)
Please sign in to comment.