Skip to content

Commit

Permalink
fix: improve TypeScript types (#841)
Browse files Browse the repository at this point in the history
* fix: improve TypeScript types

* fix: disable allowSyntheticDefaultImports and esModuleInterop

* fix: improve HeadersInit types (js/ts)

* fix: fully match types to build index.cjs

* fix: allow Iterable<Iterable<string>> in HeadersInit
  • Loading branch information
SomaticIT committed May 29, 2020
1 parent 769f75d commit e6bfe4d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 61 deletions.
133 changes: 76 additions & 57 deletions @types/index.d.ts
Expand Up @@ -3,10 +3,16 @@
/* eslint-disable no-var, import/no-mutable-exports */

import {Agent} from 'http';
import {AbortSignal} from 'abort-controller';
import Blob from 'fetch-blob';
import * as Blob from 'fetch-blob';

type HeadersInit = Headers | string[][] | Record<string, string>;
type AbortSignal = {
readonly aborted: boolean;

addEventListener(type: "abort", listener: (this: AbortSignal, ev: Event) => any, options?: boolean | { passive?: boolean; once?: boolean; }): void;
removeEventListener(type: "abort", listener: (this: AbortSignal, ev: Event) => any, options?: boolean | { capture?: boolean; }): void;
};

type HeadersInit = Headers | Record<string, string> | Iterable<readonly [string, string]> | Iterable<Iterable<string>>;

/**
* This Fetch API interface allows you to perform various actions on HTTP request and response headers.
Expand All @@ -15,38 +21,36 @@ type HeadersInit = Headers | string[][] | Record<string, string>;
* You can add to this using methods like append() (see Examples.)
* In all methods of this interface, header names are matched by case-insensitive byte sequence.
* */
interface Headers {
append: (name: string, value: string) => void;
delete: (name: string) => void;
get: (name: string) => string | null;
has: (name: string) => boolean;
set: (name: string, value: string) => void;
forEach: (
declare class Headers {
constructor(init?: HeadersInit);

append(name: string, value: string): void;
delete(name: string): void;
get(name: string): string | null;
has(name: string): boolean;
set(name: string, value: string): void;
forEach(
callbackfn: (value: string, key: string, parent: Headers) => void,
thisArg?: any
) => void;
): void;

[Symbol.iterator]: () => IterableIterator<[string, string]>;
[Symbol.iterator](): IterableIterator<[string, string]>;
/**
* Returns an iterator allowing to go through all key/value pairs contained in this object.
*/
entries: () => IterableIterator<[string, string]>;
entries(): IterableIterator<[string, string]>;
/**
* Returns an iterator allowing to go through all keys of the key/value pairs contained in this object.
*/
keys: () => IterableIterator<string>;
keys(): IterableIterator<string>;
/**
* Returns an iterator allowing to go through all values of the key/value pairs contained in this object.
*/
values: () => IterableIterator<string>;
values(): IterableIterator<string>;

/** Node-fetch extension */
raw: () => Record<string, string[]>;
raw(): Record<string, string[]>;
}
declare var Headers: {
prototype: Headers;
new (init?: HeadersInit): Headers;
};

interface RequestInit {
/**
Expand Down Expand Up @@ -94,23 +98,26 @@ type BodyInit =
| URLSearchParams
| NodeJS.ReadableStream
| string;
interface Body {
type BodyType = { [K in keyof Body]: Body[K] };
declare class Body {
constructor(body?: BodyInit, opts?: { size?: number });

readonly body: NodeJS.ReadableStream | null;
readonly bodyUsed: boolean;
readonly size: number;
buffer: () => Promise<Buffer>;
arrayBuffer: () => Promise<ArrayBuffer>;
blob: () => Promise<Blob>;
json: () => Promise<unknown>;
text: () => Promise<string>;

buffer(): Promise<Buffer>;
arrayBuffer(): Promise<ArrayBuffer>;
blob(): Promise<Blob>;
json(): Promise<unknown>;
text(): Promise<string>;
}
declare var Body: {
prototype: Body;
new (body?: BodyInit, opts?: {size?: number}): Body;
};

type RequestRedirect = 'error' | 'follow' | 'manual';
interface Request extends Body {
type RequestInfo = string | Body;
declare class Request extends Body {
constructor(input: RequestInfo, init?: RequestInit);

/**
* Returns a Headers object consisting of the headers associated with request. Note that headers added in the network layer by the user agent will not be accounted for in this object, e.g., the "Host" header.
*/
Expand All @@ -131,52 +138,64 @@ interface Request extends Body {
* Returns the URL of request as a string.
*/
readonly url: string;
clone: () => Request;
clone(): Request;
}
type RequestInfo = string | Body;
declare var Request: {
prototype: Request;
new (input: RequestInfo, init?: RequestInit): Request;
};

interface Response extends Body {
declare class Response extends Body {
constructor(body?: BodyInit | null, init?: ResponseInit);

readonly headers: Headers;
readonly ok: boolean;
readonly redirected: boolean;
readonly status: number;
readonly statusText: string;
readonly url: string;
clone: () => Response;
clone(): Response;
}

declare var Response: {
prototype: Response;
new (body?: BodyInit | null, init?: ResponseInit): Response;
};

declare function fetch(url: RequestInfo, init?: RequestInit): Promise<Response>;
declare class FetchError extends Error {
constructor(message: string, type: string, systemError?: object);

declare namespace fetch {
function isRedirect(code: number): boolean;
}

interface FetchError extends Error {
name: 'FetchError';
[Symbol.toStringTag]: 'FetchError';
type: string;
code?: string;
errno?: string;
}
declare var FetchError: {
prototype: FetchError;
new (message: string, type: string, systemError?: object): FetchError;
};

export class AbortError extends Error {
declare class AbortError extends Error {
type: string;
name: 'AbortError';
[Symbol.toStringTag]: 'AbortError';
}

export {Headers, Request, Response, FetchError};
export default fetch;

declare function fetch(url: RequestInfo, init?: RequestInit): Promise<Response>;
declare class fetch {
static default: typeof fetch;
}
declare namespace fetch {
export function isRedirect(code: number): boolean;

export {
HeadersInit,
Headers,

RequestInit,
RequestRedirect,
RequestInfo,
Request,

BodyInit,

ResponseInit,
Response,

FetchError,
AbortError
};

export interface Body extends BodyType { }
}

export = fetch;
42 changes: 40 additions & 2 deletions @types/index.test-d.ts
@@ -1,5 +1,9 @@
import {expectType} from 'tsd';
import fetch, {Request, Response, Headers, FetchError, AbortError} from '.';
import {expectType, expectAssignable} from 'tsd';
import AbortController from 'abort-controller';

import fetch, {Request, Response, Headers, Body, FetchError, AbortError} from '.';
import * as _fetch from '.';
import __fetch = require('.');

async function run() {
const getRes = await fetch('https://bigfile.com/test.zip');
Expand Down Expand Up @@ -56,8 +60,42 @@ async function run() {
}
}

// export *
const wildRes = await _fetch('https://google.com');
expectType<boolean>(wildRes.ok);
expectType<number>(wildRes.size);
expectType<number>(wildRes.status);
expectType<string>(wildRes.statusText);
expectType<() => Response>(wildRes.clone);

// export = require
const reqRes = await __fetch('https://google.com');
expectType<boolean>(reqRes.ok);
expectType<number>(reqRes.size);
expectType<number>(reqRes.status);
expectType<string>(reqRes.statusText);
expectType<() => Response>(reqRes.clone);

// Others
const response = new Response();
expectType<string>(response.url);
expectAssignable<Body>(response);

const abortController = new AbortController()
const request = new Request('url', { signal: abortController.signal });
expectAssignable<Body>(request);

new Headers({'Header': 'value'});
// new Headers(['header', 'value']); // should not work
new Headers([['header', 'value']]);
new Headers(new Headers());
new Headers([
new Set(['a', '1']),
['b', '2'],
new Map([['a', null], ['3', null]]).keys()
]);

fetch.isRedirect = (code: number) => true;
}

run().finally(() => {
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -78,7 +78,8 @@
"lib": [
"es2018"
],
"allowSyntheticDefaultImports": true
"allowSyntheticDefaultImports": false,
"esModuleInterop": false
}
},
"xo": {
Expand Down
2 changes: 1 addition & 1 deletion src/headers.js
Expand Up @@ -24,7 +24,7 @@ function validateValue(value) {
}

/**
* @typedef {Headers | Record<string, string> | Iterable<readonly [string, string]> | Iterable<string>[]} HeadersInit
* @typedef {Headers | Record<string, string> | Iterable<readonly [string, string]> | Iterable<Iterable<string>>} HeadersInit
*/

/**
Expand Down

0 comments on commit e6bfe4d

Please sign in to comment.