Skip to content

Commit

Permalink
Merge pull request #159 from developit/typescript-support
Browse files Browse the repository at this point in the history
Add full TypeScript definitions and export them, add basic TS tests.
  • Loading branch information
developit authored Dec 31, 2022
2 parents 3fd53f5 + 51359d9 commit fb69127
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 42 deletions.
1 change: 1 addition & 0 deletions .github/workflows/size.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ jobs:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: 18
node-version-file: "package.json"
cache: "npm"
cache-dependency-path: "**/package-lock.json"
Expand Down
60 changes: 59 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 10 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"jsnext:main": "./dist/unfetch.mjs",
"umd:main": "./dist/unfetch.umd.js",
"scripts": {
"test": "eslint && NODE_OPTIONS=--experimental-vm-modules jest",
"test": "eslint && tsc -p . --noEmit && NODE_OPTIONS=--experimental-vm-modules jest",
"build": "microbundle src/index.mjs -f cjs,esm,umd && microbundle polyfill/polyfill.mjs -o polyfill/index.js -f cjs --no-sourcemap",
"prepare": "npm run -s build",
"release": "cross-var npm run build -s && cross-var git commit -am $npm_package_version && cross-var git tag $npm_package_version && git push && git push --tags && npm publish"
Expand Down Expand Up @@ -53,37 +53,41 @@
"url": "http://localhost/"
},
"testMatch": [
"<rootDir>/test/**/*.test.?(m)js?(x)"
"<rootDir>/test/**/*.test.?(m)[jt]s?(x)"
],
"setupFiles": [
"<rootDir>/test/_setup.js"
],
"moduleFileExtensions": [
"mjs",
"js"
"js",
"ts"
],
"transform": {
"^.+\\.m?jsx?$": "babel-jest"
"^.+\\.m?[jt]sx?$": "babel-jest"
}
},
"babel": {
"env": {
"test": {
"presets": [
"@babel/preset-env"
"@babel/preset-env",
"@babel/preset-typescript"
]
}
}
},
"devDependencies": {
"@babel/core": "^7.20.7",
"@babel/preset-env": "^7.20.2",
"@babel/preset-typescript": "^7.18.6",
"@types/jest": "^29.2.5",
"@types/node": "^18.11.18",
"cross-var": "^1.1.0",
"eslint": "^8.30.0",
"eslint-config-developit": "^1.2.0",
"jest": "^29.3.1",
"microbundle": "^0.15.1"
"microbundle": "^0.15.1",
"typescript": "^4.9.4"
}
}
127 changes: 92 additions & 35 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,102 @@
import {
Body as NodeBody,
Headers as NodeHeaders,
Request as NodeRequest,
Response as NodeResponse,
RequestInit as NodeRequestInit
Body as NodeBody,
Headers as NodeHeaders,
Request as NodeRequest,
Response as NodeResponse,
RequestInit as NodeRequestInit,
} from "node-fetch";

declare namespace unfetch {
export type IsomorphicHeaders = Headers | NodeHeaders;
export type IsomorphicBody = Body | NodeBody;
export type IsomorphicResponse = Response | NodeResponse;
export type IsomorphicRequest = Request | NodeRequest;
export type IsomorphicRequestInit = RequestInit | NodeRequestInit;
/** @augments Headers */
export interface UnfetchHeaders {
keys: () => string[];
entries: () => [string, string][];
get: (key: string) => string | null;
has: (key: string) => boolean;

/** @deprecated not supported by unfetch */
append: never;
/** @deprecated not supported by unfetch */
delete: never;
/** @deprecated not supported by unfetch */
forEach: never;
/** @deprecated not supported by unfetch */
set: never;
/** @deprecated not supported by unfetch */
values: never;
/** @deprecated not supported by unfetch */
[Symbol.iterator]: never;
}

type UnfetchResponse = {
ok: boolean,
statusText: string,
status: number,
url: string,
text: () => Promise<string>,
json: () => Promise<any>,
blob: () => Promise<Blob>,
clone: () => UnfetchResponse,
headers: {
keys: () => string[],
entries: () => Array<[string, string]>,
get: (key: string) => string | undefined,
has: (key: string) => boolean,
}
/** @augments Response */
export interface UnfetchResponse {
ok: boolean;
statusText: string;
status: number;
url: string;
text: () => Promise<string>;
json: () => Promise<any>;
blob: () => Promise<Blob>;
clone: () => UnfetchResponse;
headers: UnfetchHeaders;

/** @deprecated not supported by unfetch */
arrayBuffer: never;
/** @deprecated not supported by unfetch */
body: never;
/** @deprecated not supported by unfetch */
bodyUsed: never;
/** @deprecated not supported by unfetch */
formData: never;
/** @deprecated not supported by unfetch */
redirected: never;
/** @deprecated not supported by unfetch */
type: never;
}

/** @augments RequestInit */
export interface UnfetchRequestInit {
method?: string;
headers?: Record<string, string>;
credentials?: "include" | "omit";
body?: Parameters<XMLHttpRequest["send"]>[0];

/** @deprecated not supported by unfetch */
cache?: never;
/** @deprecated not supported by unfetch */
integrity?: never;
/** @deprecated not supported by unfetch */
keepalive?: never;
/** @deprecated not supported by unfetch */
mode?: never;
/** @deprecated not supported by unfetch */
redirect?: never;
/** @deprecated not supported by unfetch */
referrer?: never;
/** @deprecated not supported by unfetch */
referrerPolicy?: never;
/** @deprecated not supported by unfetch */
signal?: never;
/** @deprecated not supported by unfetch */
window?: never;
}

type Unfetch = (
url: string,
options?: {
method?: string,
headers?: Record<string, string>,
credentials?: 'include' | 'omit',
body?: Parameters<XMLHttpRequest["send"]>[0]
}
) => Promise<UnfetchResponse>
export namespace Unfetch {
export type IsomorphicHeaders = Headers | NodeHeaders;
export type IsomorphicBody = Body | NodeBody;
export type IsomorphicResponse = Response | NodeResponse;
export type IsomorphicRequest = Request | NodeRequest;
export type IsomorphicRequestInit = RequestInit | NodeRequestInit;

export type Headers = UnfetchHeaders | globalThis.Headers;
export type Body = globalThis.Body;
export type Response = UnfetchResponse | globalThis.Response;
export type Request = UnfetchRequestInit | globalThis.Request;
export type RequestInit = UnfetchRequestInit | globalThis.RequestInit;
}

export interface Unfetch {
(url: string | URL, options?: UnfetchRequestInit): Promise<UnfetchResponse>;
}

declare const unfetch: Unfetch;

Expand Down
58 changes: 58 additions & 0 deletions test/typescript.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import unfetch, { Unfetch } from "..";
import isomorphicUnfetch from "../packages/isomorphic-unfetch";

describe("TypeScript", () => {
describe("browser", () => {
beforeAll(() => {
function XMLHttpRequest() {
const res = {
setRequestHeader: jest.fn(),
getAllResponseHeaders: jest.fn().mockReturnValue(""),
getResponseHeader: jest.fn().mockReturnValue(""),
open: jest.fn((method, url) => {
res.responseURL = url;
}),
send: jest.fn(),
status: 200,
statusText: "OK",
get responseText() {
return this.responseURL.replace(/^data:\,/, "");
},
responseURL: null,
onload: () => {},
};
setTimeout(() => res.onload());
return res;
}

// @ts-ignore-next-line
global.XMLHttpRequest = jest.fn(XMLHttpRequest);
});

it("should have valid TypeScript types", async () => {
const res: Unfetch.Response = await unfetch("data:,test");
const text = await res.text();
expect(text).toBe("test");
});

// This fails because we're abusing Arrays as iterables:
// it("should allow cast to Response", async () => {
// const res: Response = await unfetch("data:,test");
// const r = res.headers.keys()[0]
// });
});

describe("isomorphic-unfetch", () => {
it("should allow use of standard types like Response", async () => {
const res: Response = await isomorphicUnfetch(new URL("data:,test"));
const blob: Blob = await res.blob();
});

it("should accept Headers", async () => {
isomorphicUnfetch("data:,test", {
headers: new Headers({ a: "b" }),
mode: "cors",
});
});
});
});
12 changes: 12 additions & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"compilerOptions": {
"noEmit": true,
"checkJs": true,
"target": "ESNext",
"moduleResolution": "Node",
"allowSyntheticDefaultImports": true,
},
"include": [
"./**/*.ts"
]
}

0 comments on commit fb69127

Please sign in to comment.