Repository: wheresrhys/fetch-mock
Pull request #794 (2024-08-09 07:59:54)

Convert core to typescript


Patch:
diff --git a/eslint.config.js b/eslint.config.js
index a1de7a9..6af7c35 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -17,7 +17,8 @@ export default tseslint.config(
 	},
 	{
 		rules: {
-			'no-prototype-builtins': 0
+			'no-prototype-builtins': 0,
+			'@typescript-eslint/no-wrapper-object-types': 0
 		},
 		languageOptions: {
 			globals: {
diff --git a/jsconfig.json b/jsconfig.json
index 7d092d6..3446a5a 100644
--- a/jsconfig.json
+++ b/jsconfig.json
@@ -19,10 +19,8 @@
         "downlevelIteration": true,
         "forceConsistentCasingInFileNames": true,
         "declaration": true,
-        "emitDeclarationOnly": true,
-        "outDir": "packages/core/types",
         "skipLibCheck": true,
-        "noEmit": false,
+        "noEmit": true,
         "target": "es2021",
         "allowImportingTsExtensions": true,
         "removeComments": true
diff --git a/packages/core/src/CallHistory.js b/packages/core/src/CallHistory.ts
similarity index 54%
rename from packages/core/src/CallHistory.js
rename to packages/core/src/CallHistory.ts
index f149cfb..88336de 100644
--- a/packages/core/src/CallHistory.js
+++ b/packages/core/src/CallHistory.ts
@@ -1,84 +1,55 @@
-//@type-check
-/** @typedef {import('./Route.js').RouteConfig} RouteConfig */
-/** @typedef {import('./Route.js').RouteName} RouteName */
-/** @typedef {import('./RequestUtils.js').NormalizedRequestOptions} NormalizedRequestOptions */
-/** @typedef {import('./Matchers.js').RouteMatcher} RouteMatcher */
-/** @typedef {import('./FetchMock.js').FetchMockConfig} FetchMockConfig */
-import { createCallLogFromUrlAndOptions } from './RequestUtils.js';
-import { isUrlMatcher } from './Matchers.js';
-import Route from './Route.js';
+import { createCallLogFromUrlAndOptions, NormalizedRequestOptions } from './RequestUtils.js';
+import { isUrlMatcher, RouteMatcher } from './Matchers.js';
+import Route, {RouteConfig, RouteName} from './Route.js';
 import Router from './Router.js';
-
-/**
- * @typedef CallLog
- * @property {any[]} args
- * @property {string} url
- * @property {NormalizedRequestOptions} options
- * @property {Request} [request]
- * @property {AbortSignal} [signal]
- * @property {Route} [route]
- * @property {Response} [response]
- * @property {Object.<string, string>} [expressParams]
- * @property {URLSearchParams} [queryParams]
- * @property {Promise<any>[]} pendingPromises
- */
-
-/** @typedef {'matched'} Matched */
-/** @typedef {'unmatched'} Unmatched */
-/** @typedef  {RouteName | Matched| Unmatched| boolean | RouteMatcher } CallHistoryFilter*/
-
-/**
- *
- * @param {CallHistoryFilter} filter
- * @returns {filter is RouteName}
- */
-const isName = (filter) =>
+import type { FetchMockConfig } from "./FetchMock.js";
+
+export type Matched = "matched";
+export type Unmatched = "unmatched";
+export type CallHistoryFilter = RouteName | Matched | Unmatched | boolean | RouteMatcher;
+export type CallLog = {
+	args: unknown[];
+	url: string;
+	options: NormalizedRequestOptions;
+	request?: Request;
+	signal?: AbortSignal;
+	route?: Route;
+	response?: Response;
+	expressParams?: {
+		[x: string]: string;
+	};
+	queryParams?: URLSearchParams;
+	pendingPromises: Promise<unknown>[];
+};
+
+const isName = (filter: CallHistoryFilter): filter is RouteName =>
 	typeof filter === 'string' &&
 	/^[\da-zA-Z-]+$/.test(filter) &&
 	!['matched', 'unmatched'].includes(filter);
 
-/**
- *
- * @param {CallHistoryFilter} filter
- * @returns {filter is (Matched| Unmatched| boolean)}
- */
-const isMatchedOrUnmatched = (filter) =>
+const isMatchedOrUnmatched = (filter: CallHistoryFilter): filter is (Matched | Unmatched | boolean) =>
 	typeof filter === 'boolean' ||
-	/** @type {CallHistoryFilter[]}*/ (['matched', 'unmatched']).includes(filter);
+	(['matched', 'unmatched']).includes(filter as string);
 
 class CallHistory {
-	/**
-	 * @param {FetchMockConfig} config
-	 * @param {Router} router
-	 */
-	constructor(config, router) {
-		/** @type {CallLog[]} */
+	callLogs: CallLog[];
+	config: FetchMockConfig;
+	router: Router;
+	constructor(config: FetchMockConfig, router: Router) {
 		this.callLogs = [];
 		this.config = config;
 		this.router = router;
 	}
-	/**
-	 *
-	 * @param {CallLog} callLog
-	 */
-	recordCall(callLog) {
+	recordCall(callLog: CallLog) {
 		this.callLogs.push(callLog);
 	}
 
-	/**
-	 * @returns {void}
-	 */
 	clear() {
 		this.callLogs.forEach(({ route }) => route.reset());
 		this.callLogs = [];
 	}
 
-	/**
-	 *
-	 * @param {boolean} [waitForResponseMethods]
-	 * @returns {Promise<void>}
-	 */
-	async flush(waitForResponseMethods) {
+	async flush(waitForResponseMethods?: boolean): Promise<void> {
 		const queuedPromises = this.callLogs.flatMap(
 			(call) => call.pendingPromises,
 		);
@@ -91,14 +62,7 @@ class CallHistory {
 			await this.flush();
 		}
 	}
-
-	/**
-	 *
-	 * @param {CallHistoryFilter} filter
-	 * @param {RouteConfig} options
-	 * @returns {CallLog[]}
-	 */
-	calls(filter, options) {
+	calls(filter: CallHistoryFilter, options: RouteConfig): CallLog[]{
 		let calls = [...this.callLogs];
 		if (typeof filter === 'undefined' && !options) {
 			return calls;
@@ -106,11 +70,11 @@ class CallHistory {
 
 		if (isMatchedOrUnmatched(filter)) {
 			if (
-				/** @type {CallHistoryFilter[]} */ ([true, 'matched']).includes(filter)
+				([true, 'matched'] as CallHistoryFilter[]).includes(filter)
 			) {
 				calls = calls.filter(({ route }) => !route.config.isFallback);
 			} else if (
-				/** @type {CallHistoryFilter[]} */ ([false, 'unmatched']).includes(
+				([false, 'unmatched'] as CallHistoryFilter[]).includes(
 					filter,
 				)
 			) {
@@ -150,30 +114,14 @@ class CallHistory {
 
 		return calls;
 	}
-	/**
-	 *
-	 * @param {CallHistoryFilter} filter
-	 * @param {RouteConfig} options
-	 * @returns {boolean}
-	 */
-	called(filter, options) {
+	called(filter: CallHistoryFilter, options: RouteConfig): boolean {
 		return Boolean(this.calls(filter, options).length);
 	}
-	/**
-	 *
-	 * @param {CallHistoryFilter} filter
-	 * @param {RouteConfig} options
-	 * @returns {CallLog}
-	 */
-	lastCall(filter, options) {
+	lastCall(filter: CallHistoryFilter, options: RouteConfig): CallLog | void {
 		return this.calls(filter, options).pop();
 	}
 
-	/**
-	 * @param {RouteName|RouteName[]} [routeNames]
-	 * @returns {boolean}
-	 */
-	done(routeNames) {
+	done(routeNames?: RouteName | RouteName[]): boolean {
 		let routesToCheck = this.router.routes;
 		if (routeNames) {
 			routeNames = Array.isArray(routeNames) ? routeNames : [routeNames];
diff --git a/packages/core/src/FetchMock.js b/packages/core/src/FetchMock.js
deleted file mode 100644
index f4fdd8c..0000000
--- a/packages/core/src/FetchMock.js
+++ /dev/null
@@ -1,275 +0,0 @@
-//@type-check
-import Router from './Router.js';
-import Route from './Route.js';
-import CallHistory from './CallHistory.js';
-import * as requestUtils from './RequestUtils.js';
-/** @typedef {import('./Router.js').RouteMatcher} RouteMatcher */
-/** @typedef {import('./Route.js').RouteName} RouteName */
-/** @typedef {import('./Route.js').UserRouteConfig} UserRouteConfig */
-/** @typedef {import('./Router.js').RouteResponse} RouteResponse */
-/** @typedef {import('./Matchers.js').MatcherDefinition} MatcherDefinition */
-/** @typedef {import('./CallHistory.js').CallLog} CallLog */
-/** @typedef {import('./Route.js').RouteResponseFunction} RouteResponseFunction */
-
-/**
- * @typedef FetchMockGlobalConfig
- * @property {boolean} [sendAsJson]
- * @property {boolean} [includeContentLength]
- * @property {boolean} [matchPartialBody]
- */
-
-/**
- * @typedef FetchImplementations
- * @property {typeof fetch} [fetch]
- * @property {typeof Headers} [Headers]
- * @property {typeof Request} [Request]
- * @property {typeof Response} [Response]
- */
-
-/** @typedef {FetchMockGlobalConfig & FetchImplementations} FetchMockConfig */
-
-/** @type {FetchMockConfig} */
-const defaultConfig = {
-	includeContentLength: true,
-	sendAsJson: true,
-	matchPartialBody: false,
-	Request: globalThis.Request,
-	Response: globalThis.Response,
-	Headers: globalThis.Headers,
-	fetch: globalThis.fetch,
-};
-
-/**
- *
- * @param {UserRouteConfig} shorthandOptions
- */
-const defineShorthand = (shorthandOptions) => {
-	/**
-	 * @overload
-	 * @param {UserRouteConfig} matcher
-	 * @this {FetchMock}
-	 * @returns {FetchMock}
-	 */
-
-	/**
-	 * @overload
-	 * @param {RouteMatcher } matcher
-	 * @param {RouteResponse} response
-	 * @param {UserRouteConfig | string} [options]
-	 * @this {FetchMock}
-	 * @returns {FetchMock}
-	 */
-
-	/**
-	 * @param {RouteMatcher | UserRouteConfig} matcher
-	 * @param {RouteResponse} [response]
-	 * @param {UserRouteConfig | string} [options]
-	 * @this {FetchMock}
-	 * @returns {FetchMock}
-	 */
-	return function (matcher, response, options) {
-		return this.route(
-			//@ts-ignore
-			matcher,
-			response,
-			Object.assign(options || {}, shorthandOptions),
-		);
-	};
-};
-/**
- *
- * @param {UserRouteConfig} shorthandOptions
- */
-const defineGreedyShorthand = (shorthandOptions) => {
-	/**
-	 * @param {RouteResponse} response
-	 * @param {UserRouteConfig | string} [options]
-	 * @this {FetchMock}
-	 * @returns {FetchMock}
-	 */
-	return function (response, options) {
-		return this.route(
-			'*',
-			response,
-			Object.assign(options || {}, shorthandOptions),
-		);
-	};
-};
-
-export class FetchMock {
-	/**
-	 *
-	 * @param {FetchMockConfig} config
-	 * @param {Router} [router]
-	 */
-	constructor(config, router) {
-		this.config = config;
-		this.router = new Router(this.config, {
-			routes: router ? [...router.routes] : [],
-			fallbackRoute: router ? router.fallbackRoute : null,
-		});
-		this.callHistory = new CallHistory(this.config, this.router);
-	}
-	/**
-	 *
-	 * @returns {FetchMock}
-	 */
-	createInstance() {
-		return new FetchMock({ ...this.config }, this.router);
-	}
-	/**
-	 *
-	 * @param {string | URL | Request} requestInput
-	 * @param {RequestInit} [requestInit]
-	 * @this {FetchMock}
-	 * @returns {Promise<Response>}
-	 */
-	async fetchHandler(requestInput, requestInit) {
-		// TODO move into router
-		let callLog;
-
-		if (requestInput instanceof this.config.Request) {
-			callLog = await requestUtils.createCallLogFromRequest(
-				requestInput,
-				requestInit,
-			);
-		} else {
-			callLog = requestUtils.createCallLogFromUrlAndOptions(
-				requestInput,
-				requestInit,
-			);
-		}
-
-		this.callHistory.recordCall(callLog);
-		const responsePromise = this.router.execute(callLog);
-		callLog.pendingPromises.push(responsePromise);
-		return responsePromise;
-	}
-	/**
-	 * @overload
-	 * @param {UserRouteConfig} matcher
-	 * @returns {FetchMock}
-	 */
-	/**
-	 * @overload
-	 * @param {RouteMatcher } matcher
-	 * @param {RouteResponse} response
-	 * @param {UserRouteConfig | string} [options]
-	 * @returns {FetchMock}
-	 */
-	/**
-	 * @param {RouteMatcher | UserRouteConfig} matcher
-	 * @param {RouteResponse} [response]
-	 * @param {UserRouteConfig | string} [options]
-	 * @this {FetchMock}
-	 */
-	route(matcher, response, options) {
-		this.router.addRoute(matcher, response, options);
-		return this;
-	}
-	/**
-	 *
-	 * @param {RouteResponse} [response]
-	 * @this {FetchMock}
-	 */
-	catch(response) {
-		this.router.setFallback(response);
-		return this;
-	}
-	/**
-	 *
-	 * @param {MatcherDefinition} matcher
-	 */
-	 
-	defineMatcher(matcher) {
-		Route.defineMatcher(matcher);
-	}
-	/**
-	 *
-	 * @param {object} [options]
-	 * @param {string[]} [options.names]
-	 * @param {boolean} [options.includeSticky]
-	 * @param {boolean} [options.includeFallback]
-	 * @this {FetchMock}
-	 */
-	removeRoutes(options) {
-		this.router.removeRoutes(options);
-		return this;
-	}
-	/**
-	 * @this {FetchMock}
-	 */
-	clearHistory() {
-		this.callHistory.clear();
-		return this;
-	}
-	sticky = defineShorthand({ sticky: true });
-	once = defineShorthand({ repeat: 1 });
-	any = defineGreedyShorthand({});
-	anyOnce = defineGreedyShorthand({ repeat: 1 });
-	get = defineShorthand({ method: 'get' });
-	getOnce = defineShorthand({ method: 'get', repeat: 1 });
-	post = defineShorthand({ method: 'post' });
-	postOnce = defineShorthand({ method: 'post', repeat: 1 });
-	put = defineShorthand({ method: 'put' });
-	putOnce = defineShorthand({ method: 'put', repeat: 1 });
-	delete = defineShorthand({ method: 'delete' });
-	deleteOnce = defineShorthand({ method: 'delete', repeat: 1 });
-	head = defineShorthand({ method: 'head' });
-	headOnce = defineShorthand({ method: 'head', repeat: 1 });
-	patch = defineShorthand({ method: 'patch' });
-	patchOnce = defineShorthand({ method: 'patch', repeat: 1 });
-}
-
-class FetchMockStandalone extends FetchMock {
-	/** @type {typeof fetch} */
-	#originalFetch = null;
-	/**
-	 * @this {FetchMockStandalone}
-	 */
-	mockGlobal() {
-		globalThis.fetch = this.fetchHandler.bind(this);
-		return this;
-	}
-	/**
-	 * @this {FetchMockStandalone}
-	 */
-	unmockGlobal() {
-		globalThis.fetch = this.config.fetch;
-		return this;
-	}
-
-	/**
-	 * @param {RouteMatcher | UserRouteConfig} [matcher]
-	 * @param {RouteName} [name]
-	 * @this {FetchMockStandalone}
-	 */
-	spy(matcher, name) {
-		if (matcher) {
-			// @ts-ignore
-			this.route(matcher, ({ args }) => this.config.fetch(...args), name);
-		} else {
-			// @ts-ignore
-			this.catch(({ args }) => this.config.fetch(...args));
-		}
-
-		return this;
-	}
-	/**
-	 * @this {FetchMockStandalone}
-	 */
-	spyGlobal() {
-		this.mockGlobal();
-		return this.spy();
-	}
-
-	createInstance() {
-		return new FetchMockStandalone({ ...this.config }, this.router);
-	}
-}
-
-const fetchMock = new FetchMockStandalone({
-	...defaultConfig,
-}).createInstance();
-
-export default fetchMock;
diff --git a/packages/core/src/FetchMock.ts b/packages/core/src/FetchMock.ts
new file mode 100644
index 0000000..0509dfe
--- /dev/null
+++ b/packages/core/src/FetchMock.ts
@@ -0,0 +1,174 @@
+import Router from './Router.js';
+import Route, { RouteName, UserRouteConfig, RouteResponse } from './Route.js';
+import { MatcherDefinition, RouteMatcher } from './Matchers.js';
+import CallHistory from './CallHistory.js';
+import * as requestUtils from './RequestUtils.js';
+
+export type FetchMockGlobalConfig = {
+	sendAsJson?: boolean;
+	includeContentLength?: boolean;
+	matchPartialBody?: boolean;
+};
+export type FetchImplementations = {
+	fetch?: typeof fetch;
+	Headers?: typeof Headers;
+	Request?: typeof Request;
+	Response?: typeof Response;
+};
+export type FetchMockConfig = FetchMockGlobalConfig & FetchImplementations;
+
+/** @type {FetchMockConfig} */
+const defaultConfig = {
+	includeContentLength: true,
+	sendAsJson: true,
+	matchPartialBody: false,
+	Request: globalThis.Request,
+	Response: globalThis.Response,
+	Headers: globalThis.Headers,
+	fetch: globalThis.fetch,
+};
+
+const defineShorthand = (shorthandOptions: UserRouteConfig) => {
+
+	function shorthand (this: FetchMock, matcher: UserRouteConfig): FetchMock;
+	function shorthand (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
+	function shorthand (this: FetchMock, matcher: (RouteMatcher | UserRouteConfig), response?: RouteResponse, options?: (UserRouteConfig | string)): FetchMock {
+
+		return this.route(
+			//@ts-expect-error TODO research how to overload an overload
+			matcher,
+			response,
+			Object.assign(options || {}, shorthandOptions),
+		);
+	};
+
+	return shorthand;
+};
+const defineGreedyShorthand = (shorthandOptions: UserRouteConfig) => {
+	return function(this: FetchMock, response: RouteResponse, options?: UserRouteConfig | string): FetchMock {
+		return this.route(
+			'*',
+			response,
+			Object.assign(options || {}, shorthandOptions),
+		);
+	};
+};
+
+export class FetchMock {
+
+	config: FetchMockConfig;
+	router: Router;
+	callHistory: CallHistory;
+
+	constructor(config: FetchMockConfig, router?: Router) {
+		this.config = config;
+		this.router = new Router(this.config, {
+			routes: router ? [...router.routes] : [],
+			fallbackRoute: router ? router.fallbackRoute : null,
+		});
+		this.callHistory = new CallHistory(this.config, this.router);
+	}
+	createInstance(): FetchMock {
+		return new FetchMock({ ...this.config }, this.router);
+	}
+	async fetchHandler(this: FetchMock, requestInput: string | URL | Request, requestInit?: RequestInit): Promise<Response> {
+		// TODO move into router
+		let callLog;
+
+		if (requestInput instanceof this.config.Request) {
+			callLog = await requestUtils.createCallLogFromRequest(
+				requestInput,
+				requestInit,
+			);
+		} else {
+			callLog = requestUtils.createCallLogFromUrlAndOptions(
+				requestInput,
+				requestInit,
+			);
+		}
+
+		this.callHistory.recordCall(callLog);
+		const responsePromise = this.router.execute(callLog);
+		callLog.pendingPromises.push(responsePromise);
+		return responsePromise;
+	}
+
+	route( matcher: UserRouteConfig): FetchMock;
+	route( matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
+	route( matcher: (RouteMatcher | UserRouteConfig), response?: RouteResponse, options?: (UserRouteConfig | string)): FetchMock {
+		this.router.addRoute(matcher, response, options);
+		return this;
+	}
+	catch(response?: RouteResponse): FetchMock {
+		this.router.setFallback(response);
+		return this;
+	}
+	defineMatcher(matcher: MatcherDefinition) {
+		Route.defineMatcher(matcher);
+	}
+	removeRoutes(options?: {
+		names?: string[];
+		includeSticky?: boolean;
+		includeFallback?: boolean;
+	}):FetchMock {
+		this.router.removeRoutes(options);
+		return this;
+	}
+	clearHistory():FetchMock {
+		this.callHistory.clear();
+		return this;
+	}
+	sticky = defineShorthand({ sticky: true });
+	once = defineShorthand({ repeat: 1 });
+	any = defineGreedyShorthand({});
+	anyOnce = defineGreedyShorthand({ repeat: 1 });
+	get = defineShorthand({ method: 'get' });
+	getOnce = defineShorthand({ method: 'get', repeat: 1 });
+	post = defineShorthand({ method: 'post' });
+	postOnce = defineShorthand({ method: 'post', repeat: 1 });
+	put = defineShorthand({ method: 'put' });
+	putOnce = defineShorthand({ method: 'put', repeat: 1 });
+	delete = defineShorthand({ method: 'delete' });
+	deleteOnce = defineShorthand({ method: 'delete', repeat: 1 });
+	head = defineShorthand({ method: 'head' });
+	headOnce = defineShorthand({ method: 'head', repeat: 1 });
+	patch = defineShorthand({ method: 'patch' });
+	patchOnce = defineShorthand({ method: 'patch', repeat: 1 });
+}
+
+class FetchMockStandalone extends FetchMock {
+	mockGlobal(this: FetchMockStandalone): FetchMockStandalone {
+		globalThis.fetch = this.fetchHandler.bind(this);
+		return this;
+	}
+	unmockGlobal(this: FetchMockStandalone): FetchMockStandalone {
+		globalThis.fetch = this.config.fetch;
+		return this;
+	}
+
+	spy(this: FetchMockStandalone, matcher?: RouteMatcher | UserRouteConfig, name?: RouteName): FetchMockStandalone {
+		if (matcher) {
+			//@ts-expect-error TODO findo out how to overload an overload
+			this.route(matcher, ({ args }) => this.config.fetch(...args), name);
+		} else {
+			//@ts-expect-error TODO findo out how to overload an overload
+			this.catch(({ args }) => this.config.fetch(...args));
+		}
+
+		return this;
+	}
+	spyGlobal(this: FetchMockStandalone): FetchMockStandalone {
+		this.mockGlobal();
+		return this.spy();
+	}
+
+	createInstance(): FetchMockStandalone {
+		return new FetchMockStandalone({ ...this.config }, this.router);
+	}
+}
+
+const fetchMock = new FetchMockStandalone({
+	...defaultConfig,
+}).createInstance();
+
+export default fetchMock;
diff --git a/packages/core/src/Matchers.js b/packages/core/src/Matchers.ts
similarity index 73%
rename from packages/core/src/Matchers.js
rename to packages/core/src/Matchers.ts
index 5c6cbbc..cd49b40 100644
--- a/packages/core/src/Matchers.js
+++ b/packages/core/src/Matchers.ts
@@ -1,54 +1,39 @@
-//@type-check
-/** @typedef {import('./Route.js').RouteConfig} RouteConfig */
-/** @typedef {import('./CallHistory.js').CallLog} CallLog */
+import { RouteConfig} from './Route.js';
+import { CallLog } from './CallHistory.js';
 import glob from 'globrex';
 import * as regexparam from 'regexparam';
 import { isSubsetOf } from 'is-subset-of';
 import { dequal as isEqual } from 'dequal';
 import { normalizeHeaders, getPath, normalizeUrl } from './RequestUtils.js';
 
-/**
- * @typedef URLMatcherObject
- * @property {string} [begin]
- * @property {string} [end]
- * @property {string} [glob]
- * @property {string} [express]
- * @property {string} [path]
- * @property {RegExp} [regexp]
- */
-/** @typedef {string | RegExp | URL | URLMatcherObject} RouteMatcherUrl */
-/** @typedef {function(string): RouteMatcherFunction} UrlMatcherGenerator */
-/** @typedef {function(CallLog): boolean} RouteMatcherFunction */
-/** @typedef {function(RouteConfig): RouteMatcherFunction} MatcherGenerator */
-/** @typedef {RouteMatcherUrl | RouteMatcherFunction} RouteMatcher */
+export type URLMatcherObject = {
+	begin?: string;
+	end?: string;
+	glob?: string;
+	express?: string;
+	path?: string;
+	regexp?: RegExp;
+};
+export type RouteMatcherUrl = string | RegExp | URL | URLMatcherObject;
+type UrlMatcherGenerator = (targetString: string) => RouteMatcherFunction;
+export type RouteMatcherFunction = (callLog: CallLog) => boolean;
+type MatcherGenerator = (route: RouteConfig) => RouteMatcherFunction;
+export type RouteMatcher = RouteMatcherUrl | RouteMatcherFunction;
 
-/**
- * @typedef MatcherDefinition
- * @property {string} name
- * @property {MatcherGenerator} matcher
- * @property  {boolean} [usesBody]
- */
+export type MatcherDefinition = {
+	name: string;
+	matcher: MatcherGenerator;
+	usesBody?: boolean;
+};
 
-/**
- * @param {RouteMatcher | RouteConfig} matcher
- * @returns {matcher is RouteMatcherUrl}
- */
-export const isUrlMatcher = (matcher) =>
+export const isUrlMatcher = (matcher: RouteMatcher | RouteConfig): matcher is RouteMatcherUrl =>
 	matcher instanceof RegExp ||
 	typeof matcher === 'string' ||
 	(typeof matcher === 'object' && 'href' in matcher);
 
-/**
- *
- * @param {RouteMatcher| RouteConfig} matcher
- * @returns {matcher is RouteMatcherFunction}
- */
-export const isFunctionMatcher = (matcher) => typeof matcher === 'function';
+export const isFunctionMatcher = (matcher: RouteMatcher | RouteConfig): matcher is RouteMatcherFunction => typeof matcher === 'function';
 
-/**
- * @type {Object.<string, UrlMatcherGenerator>}
- */
-const stringMatchers = {
+const stringMatchers: { [key: string]: UrlMatcherGenerator } = {
 	begin:
 		(targetString) =>
 		({ url }) =>
@@ -85,10 +70,7 @@ const stringMatchers = {
 		({ url }) =>
 			getPath(url) === targetString,
 };
-/**
- * @type {MatcherGenerator}
- */
-const getHeaderMatcher = ({ headers: expectedHeaders }) => {
+const getHeaderMatcher: MatcherGenerator = ({ headers: expectedHeaders }) => {
 	if (!expectedHeaders) {
 		return;
 	}
@@ -101,10 +83,8 @@ const getHeaderMatcher = ({ headers: expectedHeaders }) => {
 		);
 	};
 };
-/**
- * @type {MatcherGenerator}
- */
-const getMissingHeaderMatcher = ({
+
+const getMissingHeaderMatcher: MatcherGenerator = ({
 	missingHeaders: expectedMissingHeaders,
 }) => {
 	if (!expectedMissingHeaders) {
@@ -118,10 +98,8 @@ const getMissingHeaderMatcher = ({
 		return expectation.every((headerName) => !(headerName in lowerCaseHeaders));
 	};
 };
-/**
- * @type {MatcherGenerator}
- */
-const getMethodMatcher = ({ method: expectedMethod }) => {
+
+const getMethodMatcher: MatcherGenerator = ({ method: expectedMethod }) => {
 	if (!expectedMethod) {
 		return;
 	}
@@ -130,10 +108,8 @@ const getMethodMatcher = ({ method: expectedMethod }) => {
 		return expectedMethod === actualMethod;
 	};
 };
-/**
- * @type {MatcherGenerator}
- */
-const getQueryParamsMatcher = ({ query: passedQuery }) => {
+
+const getQueryParamsMatcher: MatcherGenerator = ({ query: passedQuery }) => {
 	if (!passedQuery) {
 		return;
 	}
@@ -178,10 +154,8 @@ const getQueryParamsMatcher = ({ query: passedQuery }) => {
 		});
 	};
 };
-/**
- * @type {MatcherGenerator}
- */
-const getExpressParamsMatcher = ({ params: expectedParams, url }) => {
+
+const getExpressParamsMatcher: MatcherGenerator = ({ params: expectedParams, url }) => {
 	if (!expectedParams) {
 		return;
 	}
@@ -197,10 +171,8 @@ const getExpressParamsMatcher = ({ params: expectedParams, url }) => {
 		);
 	};
 };
-/**
- * @type {MatcherGenerator}
- */
-const getBodyMatcher = (route) => {
+
+const getBodyMatcher: MatcherGenerator = (route) => {
 	const { body: expectedBody } = route;
 
 	if (!expectedBody) {
@@ -221,7 +193,7 @@ const getBodyMatcher = (route) => {
 			if (typeof body === 'string') {
 				sentBody = JSON.parse(body);
 			}
-		} catch (err) {}
+		} catch {} //eslint-disable-line no-empty
 
 		return (
 			sentBody &&
@@ -232,28 +204,15 @@ const getBodyMatcher = (route) => {
 	};
 };
 
-/**
- * @type {MatcherGenerator}
- */
-const getFunctionMatcher = ({ matcherFunction }) => matcherFunction;
 
-/**
- * @param {RegExp} regexp
- * @returns {RouteMatcherFunction}
- */
+const getFunctionMatcher: MatcherGenerator = ({ matcherFunction }) => matcherFunction;
+
 const getRegexpMatcher =
-	(regexp) =>
+	(regexp: RegExp): RouteMatcherFunction =>
 	({ url }) =>
 		regexp.test(url);
 
-/**
- *
- * @param {RouteConfig} route
- * @param {string} matcherUrl
- * @param {Object.<string,string>} query
- * @returns {RouteMatcherFunction}
- */
-const getFullUrlMatcher = (route, matcherUrl, query) => {
+const getFullUrlMatcher = (route: RouteConfig, matcherUrl: string, query: { [key: string]: string }): RouteMatcherFunction => {
 	// if none of the special syntaxes apply, it's just a simple string match
 	// but we have to be careful to normalize the url we check and the name
 	// of the route to allow for e.g. http://it.at.there being indistinguishable
@@ -271,10 +230,7 @@ const getFullUrlMatcher = (route, matcherUrl, query) => {
 	};
 };
 
-/**
- * @type {MatcherGenerator}
- */
-const getUrlMatcher = (route) => {
+const getUrlMatcher: MatcherGenerator = (route) => {
 	const { url: matcherUrl, query } = route;
 
 	if (matcherUrl === '*') {
@@ -317,8 +273,7 @@ const getUrlMatcher = (route) => {
 	}
 };
 
-/** @type {MatcherDefinition[]} */
-export const builtInMatchers = [
+export const builtInMatchers: MatcherDefinition[] = [
 	{ name: 'url', matcher: getUrlMatcher },
 	{ name: 'query', matcher: getQueryParamsMatcher },
 	{ name: 'method', matcher: getMethodMatcher },
diff --git a/packages/core/src/RequestUtils.js b/packages/core/src/RequestUtils.ts
similarity index 55%
rename from packages/core/src/RequestUtils.js
rename to packages/core/src/RequestUtils.ts
index 5026df4..f9eec55 100644
--- a/packages/core/src/RequestUtils.js
+++ b/packages/core/src/RequestUtils.ts
@@ -1,49 +1,38 @@
-// @type-check
+import type { CallLog } from './CallHistory.js';
 // https://stackoverflow.com/a/19709846/308237 plus data: scheme
 // split into 2 code paths as URL constructor does not support protocol-relative urls
 const absoluteUrlRX = new RegExp('^[a-z]+://|^data:', 'i');
 const protocolRelativeUrlRX = new RegExp('^//', 'i');
 
-/**
- * @typedef DerivedRequestOptions
- * @property  {string} method
- * @property  {string} [body]
- * @property  {{ [key: string]: string }} [headers]
- */
+interface DerivedRequestOptions {
+	method: string;
+	body?: string;
+	headers?: { [key: string]: string };
+}
+
+export type NormalizedRequestOptions = RequestInit | (RequestInit & DerivedRequestOptions);
 
-/** @typedef {RequestInit | (RequestInit & DerivedRequestOptions) } NormalizedRequestOptions */
-/** @typedef {import('./CallHistory.js').CallLog} CallLog */
 
-/**
- * @param {string | string | URL} url
- * @returns {string}
- */
-export function normalizeUrl(url) {
+export function normalizeUrl(url: string | String | URL) {
 	if (url instanceof URL) {
 		return url.href;
 	}
-	if (absoluteUrlRX.test(url)) {
-		return new URL(url).href;
+	const primitiveUrl: string = String(url).valueOf();
+
+	if (absoluteUrlRX.test(primitiveUrl)) {
+		return new URL(primitiveUrl).href;
 	}
-	if (protocolRelativeUrlRX.test(url)) {
-		return new URL(url, 'http://dummy').href;
+	if (protocolRelativeUrlRX.test(primitiveUrl)) {
+		return new URL(primitiveUrl, 'http://dummy').href;
 	}
-	const u = new URL(url, 'http://dummy');
-	return u.pathname + u.search;
+	const urlInstance = new URL(primitiveUrl, 'http://dummy');
+	return urlInstance.pathname + urlInstance.search;
 }
 
-/**
- *
- * @param {string | object} url
- * @param {RequestInit} options
- * @returns {CallLog}
- */
-export function createCallLogFromUrlAndOptions(url, options) {
-	/** @type {Promise<any>[]} */
-	const pendingPromises = [];
+export function createCallLogFromUrlAndOptions(url: string | String | object, options: RequestInit): CallLog {
+	const pendingPromises: Promise<unknown>[]  = [];
 	if (typeof url === 'string' || url instanceof String || url instanceof URL) {
-		// @ts-ignore - jsdoc doesn't distinguish between string and String, but typechecker complains
-		url = normalizeUrl(url);
+		const normalizedUrl: string = normalizeUrl(url);
 		const derivedOptions = options ? { ...options } : {};
 		if (derivedOptions.headers) {
 			derivedOptions.headers = normalizeHeaders(derivedOptions.headers);
@@ -53,8 +42,8 @@ export function createCallLogFromUrlAndOptions(url, options) {
 			: 'get';
 		return {
 			args: [url, options],
-			url,
-			queryParams: new URLSearchParams(getQuery(url)),
+			url: normalizedUrl,
+			queryParams: new URLSearchParams(getQuery(normalizedUrl)),
 			options: derivedOptions,
 			signal: derivedOptions.signal,
 			pendingPromises,
@@ -69,23 +58,15 @@ export function createCallLogFromUrlAndOptions(url, options) {
 	}
 }
 
-/**
- *
- * @param {Request} request
- * @param {RequestInit} options
- * @returns {Promise<CallLog>}
- */
-export async function createCallLogFromRequest(request, options) {
-	/** @type {Promise<any>[]} */
-	const pendingPromises = [];
-	/** @type {NormalizedRequestOptions} */
-	const derivedOptions = {
+export async function createCallLogFromRequest(request: Request, options: RequestInit): Promise<CallLog> {
+	const pendingPromises: Promise<unknown>[] = [];
+	const derivedOptions: NormalizedRequestOptions = {
 		method: request.method,
 	};
 
 	try {
 		derivedOptions.body = await request.clone().text();
-	} catch (err) {}
+	} catch {} // eslint-disable-line no-empty
 
 	if (request.headers) {
 		derivedOptions.headers = normalizeHeaders(request.headers);
@@ -103,34 +84,21 @@ export async function createCallLogFromRequest(request, options) {
 	return callLog;
 }
 
-/**
- * @param {string} url
- * @returns {string}
- */
-export function getPath(url) {
+export function getPath(url: string): string {
 	const u = absoluteUrlRX.test(url)
 		? new URL(url)
 		: new URL(url, 'http://dummy');
 	return u.pathname;
 }
 
-/**
- * @param {string} url
- * @returns {string}
- */
-export function getQuery(url) {
+export function getQuery(url: string): string {
 	const u = absoluteUrlRX.test(url)
 		? new URL(url)
 		: new URL(url, 'http://dummy');
 	return u.search ? u.search.substr(1) : '';
 }
 
-/**
- *
- * @param {HeadersInit | Object.<string, string |number>} headers
- * @returns {Object.<string, string>}
- */
-export const normalizeHeaders = (headers) => {
+export function normalizeHeaders(headers: HeadersInit | { [key: string]: string | number }): { [key: string]: string } {
 	let entries;
 	if (headers instanceof Headers) {
 		entries = [...headers.entries()];
diff --git a/packages/core/src/Route.js b/packages/core/src/Route.ts
similarity index 51%
rename from packages/core/src/Route.js
rename to packages/core/src/Route.ts
index 89e5caa..58e7355 100644
--- a/packages/core/src/Route.js
+++ b/packages/core/src/Route.ts
@@ -1,72 +1,60 @@
 //@type-check
 import { builtInMatchers } from './Matchers.js';
 import statusTextMap from './StatusTextMap.js';
-
-/** @typedef {import('./Matchers.js').RouteMatcher} RouteMatcher */
-/** @typedef {import('./CallHistory.js').CallLog} CallLog */
-/** @typedef {import('./Matchers.js').RouteMatcherFunction} RouteMatcherFunction */
-/** @typedef {import('./Matchers.js').RouteMatcherUrl} RouteMatcherUrl */
-/** @typedef {import('./Matchers.js').MatcherDefinition} MatcherDefinition */
-/** @typedef {import('./FetchMock.js').FetchMockGlobalConfig} FetchMockGlobalConfig */
-/** @typedef {import('./FetchMock.js').FetchImplementations} FetchImplementations */
-
-/**
- * @typedef UserRouteSpecificConfig
- * @property {RouteName} [name]
- * @property {string} [method]
- * @property {{ [key: string]: string | number  }} [headers]
- * @property {string[]} [missingHeaders]
- * @property {{ [key: string]: string }} [query]
- * @property {{ [key: string]: string }} [params]
- * @property {object} [body]
- * @property {RouteMatcherFunction} [matcherFunction]
- * @property {RouteMatcherUrl} [url]
- * @property {RouteResponse | RouteResponseFunction} [response]
- * @property {number} [repeat]
- * @property {number} [delay]
- * @property {boolean} [sticky]
- */
-
-/**
- * @typedef InternalRouteConfig
- * @property {boolean} [usesBody]
- * @property {boolean} [isFallback]
- */
-
-/** @typedef {UserRouteSpecificConfig & FetchMockGlobalConfig} UserRouteConfig */
-/** @typedef {UserRouteConfig & FetchImplementations & InternalRouteConfig} RouteConfig */
-
-/**
- * @typedef RouteResponseConfig {
- * @property {string | {}} [body]
- * @property {number} [status]
- * @property {{ [key: string]: string }} [headers]
- * @property {Error} [throws]
- * @property {string} [redirectUrl]
- * @property {ResponseInit} [options]
- */
-
-/**
- * @typedef ResponseInitUsingHeaders
- * @property {number} status
- * @property {string} statusText
- * @property {Headers} headers
- */
-
-/** @typedef {RouteResponseConfig | object}  RouteResponseObjectData */
-/** @typedef {Response | number| string | RouteResponseObjectData }  RouteResponseData */
-/** @typedef {Promise<RouteResponseData>}  RouteResponsePromise */
-/** @typedef {function(CallLog): (RouteResponseData|RouteResponsePromise)} RouteResponseFunction */
-/** @typedef {RouteResponseData | RouteResponsePromise | RouteResponseFunction} RouteResponse*/
-
-/** @typedef {string} RouteName */
-
-/**
- *
- * @param {number} [status]
- * @returns {number}
- */
-function sanitizeStatus(status) {
+import {RouteMatcherFunction, RouteMatcherUrl, MatcherDefinition} from './Matchers.js';
+import type { FetchMockGlobalConfig, FetchImplementations } from './FetchMock.js';
+import type  { CallLog } from './CallHistory.js';
+
+export type UserRouteSpecificConfig = {
+  name?: RouteName;
+  method?: string;
+  headers?: {
+    [key: string]: string | number;
+  };
+  missingHeaders?: string[];
+  query?: {
+    [key: string]: string;
+  };
+  params?: {
+    [key: string]: string;
+  };
+  body?: object;
+  matcherFunction?: RouteMatcherFunction;
+  url?: RouteMatcherUrl;
+  response?: RouteResponse | RouteResponseFunction;
+  repeat?: number;
+  delay?: number;
+  sticky?: boolean;
+};
+export type InternalRouteConfig = {
+  usesBody?: boolean;
+  isFallback?: boolean;
+};
+export type UserRouteConfig = UserRouteSpecificConfig & FetchMockGlobalConfig;
+export type RouteConfig = UserRouteConfig & FetchImplementations & InternalRouteConfig;
+export type RouteResponseConfig = {
+  body?: string | object;
+  status?: number;
+  headers?: {
+    [key: string]: string;
+  };
+  throws?: Error;
+  redirectUrl?: string;
+  options?: ResponseInit;
+};
+export type ResponseInitUsingHeaders = {
+  status: number;
+  statusText: string;
+  headers: Headers;
+};
+export type RouteResponseObjectData = RouteResponseConfig | object;
+export type RouteResponseData = Response | number | string | RouteResponseObjectData;
+export type RouteResponsePromise = Promise<RouteResponseData>;
+export type RouteResponseFunction = (arg0: CallLog) => (RouteResponseData | RouteResponsePromise);
+export type RouteResponse = RouteResponseData | RouteResponsePromise | RouteResponseFunction;
+export type RouteName = string;
+
+function sanitizeStatus(status?: number): number {
 	if (!status) {
 		return 200;
 	}
@@ -85,14 +73,12 @@ To respond with a JSON object that has status as a property assign the object to
 e.g. {"body": {"status: "registered"}}`);
 }
 
-/**
- * @class Route
- */
 class Route {
-	/**
-	 * @param {RouteConfig} config
-	 */
-	constructor(config) {
+
+  config: RouteConfig;
+  matcher: RouteMatcherFunction;
+
+  constructor(config: RouteConfig) {
 		this.config = config;
 		this.#sanitize();
 		this.#validate();
@@ -101,19 +87,7 @@ class Route {
 		this.#delayResponse();
 	}
 
-	/** @type {RouteConfig} */
-	config = {};
-	/** @type {RouteMatcherFunction=} */
-	matcher = null;
-	/**
-	 * @returns {void}
-	 */
-	 
-	reset() {}
-
-	/**
-	 * @returns {void}
-	 */
+  reset() {}
 	#validate() {
 		if (['matched', 'unmatched'].includes(this.config.name)) {
 			throw new Error(
@@ -129,17 +103,11 @@ class Route {
 			);
 		}
 	}
-	/**
-	 * @returns {void}
-	 */
 	#sanitize() {
 		if (this.config.method) {
 			this.config.method = this.config.method.toLowerCase();
 		}
 	}
-	/**
-	 * @returns {void}
-	 */
 	#generateMatcher() {
 		const activeMatchers = Route.registeredMatchers
 			.filter(({ name }) => name in this.config)
@@ -152,9 +120,6 @@ class Route {
 		this.matcher = (normalizedRequest) =>
 			activeMatchers.every(({ matcher }) => matcher(normalizedRequest));
 	}
-	/**
-	 * @returns {void}
-	 */
 	#limit() {
 		if (!this.config.repeat) {
 			return;
@@ -172,9 +137,6 @@ class Route {
 			timesLeft = this.config.repeat;
 		};
 	}
-	/**
-	 * @returns {void}
-	 */
 	#delayResponse() {
 		if (this.config.delay) {
 			const { response } = this.config;
@@ -186,12 +148,7 @@ class Route {
 		}
 	}
 
-	/**
-	 *
-	 * @param {RouteResponseConfig} responseInput
-	 * @returns {{response: Response, responseOptions: ResponseInit, responseInput: RouteResponseConfig}}
-	 */
-	constructResponse(responseInput) {
+  constructResponse(responseInput: RouteResponseConfig): { response: Response, responseOptions: ResponseInit, responseInput: RouteResponseConfig } {
 		const responseOptions = this.constructResponseOptions(responseInput);
 		const body = this.constructResponseBody(responseInput, responseOptions);
 
@@ -201,34 +158,24 @@ class Route {
 			responseInput,
 		};
 	}
-	/**
-	 *
-	 * @param {RouteResponseConfig} responseInput
-	 * @returns {ResponseInitUsingHeaders}
-	 */
-	constructResponseOptions(responseInput) {
+
+  constructResponseOptions(responseInput: RouteResponseConfig): ResponseInitUsingHeaders {
 		const options = responseInput.options || {};
 		options.status = sanitizeStatus(responseInput.status);
 		options.statusText = statusTextMap[options.status];
 		// we use Headers rather than an object because it allows us to add
 		// to them without worrying about case sensitivity of keys
 		options.headers = new this.config.Headers(responseInput.headers);
-		return /** @type {ResponseInitUsingHeaders} */ (options);
+    return options as ResponseInitUsingHeaders;
 	}
-	/**
-	 *
-	 * @param {RouteResponseConfig} responseInput
-	 * @param {ResponseInitUsingHeaders} responseOptions
-	 * @returns {string|null}
-	 */
-	constructResponseBody(responseInput, responseOptions) {
+  constructResponseBody(responseInput: RouteResponseConfig, responseOptions: ResponseInitUsingHeaders): string | null {
 		// start to construct the body
 		let body = responseInput.body;
 		// convert to json if we need to
 		if (typeof body === 'object') {
 			if (
 				this.config.sendAsJson &&
-				responseInput.body != null  
+				responseInput.body != null
 			) {
 				body = JSON.stringify(body);
 				if (!responseOptions.headers.has('Content-Type')) {
@@ -247,18 +194,14 @@ class Route {
 			}
 			return body;
 		}
-		// @ts-ignore
+		// @ts-expect-error TODO need to implement handling of non-string bodies properlyy
 		return body || null;
 	}
 
-	/**
-	 * @param {MatcherDefinition} matcher
-	 */
-	static defineMatcher(matcher) {
+  static defineMatcher(matcher: MatcherDefinition) {
 		Route.registeredMatchers.push(matcher);
 	}
-	/** @type {MatcherDefinition[]} */
-	static registeredMatchers = [];
+  static registeredMatchers: MatcherDefinition[] = [];
 }
 
 builtInMatchers.forEach(Route.defineMatcher);
diff --git a/packages/core/src/Router.js b/packages/core/src/Router.ts
similarity index 65%
rename from packages/core/src/Router.js
rename to packages/core/src/Router.ts
index 2889a7e..fb1afce 100644
--- a/packages/core/src/Router.js
+++ b/packages/core/src/Router.ts
@@ -1,22 +1,13 @@
 //@type-check
-import Route from './Route.js';
+import Route, { UserRouteConfig, RouteResponsePromise, RouteConfig, RouteResponse, RouteResponseData, RouteResponseConfig } from './Route.js';
 import { isUrlMatcher, isFunctionMatcher } from './Matchers.js';
-/** @typedef {import('./Route.js').UserRouteConfig} UserRouteConfig */
-/** @typedef {import('./Route.js').RouteConfig} RouteConfig */
-/** @typedef {import('./Route.js').RouteResponse} RouteResponse */
-/** @typedef {import('./Route.js').RouteResponseData} RouteResponseData */
-/** @typedef {import('./Route.js').RouteResponseObjectData} RouteResponseObjectData */
-/** @typedef {import('./Route.js').RouteResponseConfig} RouteResponseConfig */
-/** @typedef {import('./Route.js').RouteResponseFunction} RouteResponseFunction */
-/** @typedef {import('./Matchers.js').RouteMatcher} RouteMatcher */
-/** @typedef {import('./FetchMock.js').FetchMockConfig} FetchMockConfig */
-/** @typedef {import('./FetchMock.js')} FetchMock */
-/** @typedef {import('./CallHistory.js').CallLog} CallLog */
+import { RouteMatcher } from './Matchers.js';
+import { FetchMockConfig } from './FetchMock.js';
+import type { CallLog } from './CallHistory.js';
 
-/** @typedef {'body' |'headers' |'throws' |'status' |'redirectUrl' } ResponseConfigProp */
+export type ResponseConfigProp = "body" | "headers" | "throws" | "status" | "redirectUrl";
 
-/** @type {ResponseConfigProp[]} */
-const responseConfigProps = [
+const responseConfigProps: ResponseConfigProp[] = [
 	'body',
 	'headers',
 	'throws',
@@ -24,28 +15,15 @@ const responseConfigProps = [
 	'redirectUrl',
 ];
 
-/**
- *
- * @param {RouteConfig | string} options
- * @returns {RouteConfig}
- */
-const nameToOptions = (options) =>
-	typeof options === 'string' ? { name: options } : options;
+function nameToOptions (options: RouteConfig | string): RouteConfig {
+	return typeof options === 'string' ? { name: options } : options;
+}
 
-/**
- *
- * @param {RouteResponse} response
- * @returns {RouteResponse is RouteResponseFunction}
- */
-const isPromise = (response) =>
-	typeof (/** @type {Promise<any>} */ (response).then) === 'function';
+function isPromise (response: RouteResponse): response is RouteResponsePromise {
+	return typeof (response as Promise<unknown>).then === 'function';
+}
 
-/**
- *
- * @param {RouteResponseData} responseInput
- * @returns {RouteResponseConfig}
- */
-function normalizeResponseInput(responseInput) {
+function normalizeResponseInput(responseInput: RouteResponseData): RouteResponseConfig {
 	// If the response config looks like a status, start to generate a simple response
 	if (typeof responseInput === 'number') {
 		return {
@@ -61,15 +39,10 @@ function normalizeResponseInput(responseInput) {
 			body: responseInput,
 		};
 	}
-	return /** @type{RouteResponseConfig} */ (responseInput);
+	return responseInput as RouteResponseConfig;
 }
 
-/**
- *
- * @param {RouteResponseData} responseInput
- * @returns {boolean}
- */
-function shouldSendAsObject(responseInput) {
+function shouldSendAsObject(responseInput: RouteResponseData): boolean {
 	// if (Object.keys(responseInput).some(key => responseConfigProps.includes(key)) {
 	// 	if (Object.keys(responseInput).some(key => !responseConfigProps.includes(key)) {
 	// 		throw new Error(`Ambiguous whether response is a configuration object `)
@@ -78,12 +51,12 @@ function shouldSendAsObject(responseInput) {
 	// TODO improve this... make it less hacky and magic
 	if (
 		responseConfigProps.some(
-			(prop) => /** @type {RouteResponseConfig}*/ (responseInput)[prop],
+			(prop) => (responseInput as RouteResponseConfig)[prop],
 		)
 	) {
 		if (
 			Object.keys(responseInput).every((key) =>
-				responseConfigProps.includes(/** @type {ResponseConfigProp} */ (key)),
+				responseConfigProps.includes(key as ResponseConfigProp),
 			)
 		) {
 			return false;
@@ -93,11 +66,7 @@ function shouldSendAsObject(responseInput) {
 	return true;
 }
 
-/**
- *
- * @param {CallLog} callLog
- */
-function throwSpecExceptions({ url, options: { headers, method, body } }) {
+function throwSpecExceptions({ url, options: { headers, method, body } }: CallLog) {
 	if (headers) {
 		Object.entries(headers).forEach(([key]) => {
 			if (/\s/.test(key)) {
@@ -117,11 +86,7 @@ function throwSpecExceptions({ url, options: { headers, method, body } }) {
 	}
 }
 
-/**
- * @param {CallLog} callLog
- * @returns
- */
-const resolveUntilResponseConfig = async (callLog) => {
+const resolveUntilResponseConfig = async (callLog: CallLog) => {
 	// We want to allow things like
 	// - function returning a Promise for a response
 	// - delaying (using a timeout Promise) a function's execution to generate
@@ -129,14 +94,14 @@ const resolveUntilResponseConfig = async (callLog) => {
 	// Because of this we can't safely check for function before Promisey-ness,
 	// or vice versa. So to keep it DRY, and flexible, we keep trying until we
 	// have something that looks like neither Promise nor function
-	 
+
 	let response = callLog.route.config.response;
-	 
+
 	while (true) {
 		if (typeof response === 'function') {
 			response = response(callLog);
 		} else if (isPromise(response)) {
-			response = await response;  
+			response = await response;
 		} else {
 			return response;
 		}
@@ -144,37 +109,25 @@ const resolveUntilResponseConfig = async (callLog) => {
 };
 
 export default class Router {
-	/** @type {Route[]} */
-	routes = [];
-	/**
-	 * @param {FetchMockConfig} fetchMockConfig
-	 * @param {object} [inheritedRoutes]
-	 * @param {Route[]} [inheritedRoutes.routes]
-	 * @param {Route} [inheritedRoutes.fallbackRoute]
-	 */
-	constructor(fetchMockConfig, { routes, fallbackRoute } = {}) {
+	routes: Route[];
+	config: FetchMockConfig;
+	fallbackRoute: Route;
+	constructor(fetchMockConfig: FetchMockConfig, { routes, fallbackRoute }: { routes?: Route[], fallbackRoute?: Route } = {}) {
 		this.config = fetchMockConfig;
 		this.routes = routes || []; // TODO deep clone this??
 		this.fallbackRoute = fallbackRoute;
 	}
-	/**
-	 *
-	 * @param {Request} request
-	 * @returns {boolean}
-	 */
-	needsToReadBody(request) {
+
+	needsToReadBody(request: Request): boolean {
 		return Boolean(
 			request && this.routes.some((route) => route.config.usesBody),
 		);
 	}
 
-	/**
-	 * @param {CallLog} callLog
-	 * @returns {Promise<Response>}
-	 */
-	execute(callLog) {
+	execute(callLog: CallLog): Promise<Response> {
 		throwSpecExceptions(callLog);
 		// TODO make abort vs reject neater
+		// eslint-disable-next-line no-async-promise-executor
 		return new Promise(async (resolve, reject) => {
 			const { url, options, request, pendingPromises } = callLog;
 			if (callLog.signal) {
@@ -239,13 +192,7 @@ export default class Router {
 		});
 	}
 
-	/**
-	 *
-	 * @param {CallLog} callLog
-	 * @returns {Promise<{response: Response, responseOptions: ResponseInit, responseInput: RouteResponseConfig}>}
-	 */
-	 
-	async generateResponse(callLog) {
+	async generateResponse(callLog: CallLog): Promise<{ response: Response, responseOptions: ResponseInit, responseInput: RouteResponseConfig }> {
 		const responseInput = await resolveUntilResponseConfig(callLog);
 		// If the response is a pre-made Response, respond with it
 		if (responseInput instanceof Response) {
@@ -265,23 +212,13 @@ export default class Router {
 
 		return callLog.route.constructResponse(responseConfig);
 	}
-	/**
-	 *
-	 * @param {Response} response
-	 * @param {ResponseInit} responseConfig
-	 * @param {RouteResponseConfig} responseInput
-	 * @param {string} responseUrl
-	 * @param {Promise<any>[]} pendingPromises
-	 * @returns {Response}
-	 */
-	 
 	createObservableResponse(
-		response,
-		responseConfig,
-		responseInput,
-		responseUrl,
-		pendingPromises,
-	) {
+		response: Response,
+		responseConfig: ResponseInit,
+		responseInput: RouteResponseConfig,
+		responseUrl: string,
+		pendingPromises: Promise<unknown>[],
+	): Response {
 		// Using a proxy means we can set properties that may not be writable on
 		// the original Response. It also means we can track the resolution of
 		// promises returned by res.json(), res.text() etc
@@ -303,36 +240,33 @@ export default class Router {
 						return false;
 					}
 				}
-				//@ts-ignore
+				// TODO fix these types properly
+				//@ts-expect-error TODO probably make use of generics here
 				if (typeof response[name] === 'function') {
-					//@ts-ignore
+					//@ts-expect-error TODO probably make use of generics here
 					return new Proxy(response[name], {
 						apply: (matcherFunction, thisArg, args) => {
 							const result = matcherFunction.apply(response, args);
 							if (result.then) {
 								pendingPromises.push(
-									result.catch(/** @type {function(): void} */ () => undefined),
+									//@ts-expect-error TODO probably make use of generics here
+									result.catch(() => undefined),
 								);
 							}
 							return result;
 						},
 					});
 				}
-				//@ts-ignore
+				//@ts-expect-error TODO probably make use of generics here
 				return originalResponse[name];
 			},
 		});
 	}
 
-	/**
-	 * @param {RouteMatcher | UserRouteConfig} matcher
-	 * @param {RouteResponse} [response]
-	 * @param {UserRouteConfig | string} [nameOrOptions]
-	 * @returns {void}
-	 */
-	addRoute(matcher, response, nameOrOptions) {
-		/** @type {RouteConfig} */
-		const config = {};
+	// addRoute(matcher: UserRouteConfig): void;
+	// addRoute(matcher: RouteMatcher, response: RouteResponse, nameOrOptions?: UserRouteConfig | string): void;
+	addRoute(matcher: (RouteMatcher | UserRouteConfig), response?: RouteResponse, nameOrOptions?: (UserRouteConfig | string)): void {
+		const config: RouteConfig = {};
 		if (isUrlMatcher(matcher)) {
 			config.url = matcher;
 		} else if (isFunctionMatcher(matcher)) {
@@ -372,14 +306,11 @@ export default class Router {
 		}
 		this.routes.push(route);
 	}
-	/**
-	 * @param {RouteResponse} [response]
-	 */
-	setFallback(response) {
+	setFallback(response?: RouteResponse) {
 		if (this.fallbackRoute) {
 			console.warn(
 				'calling fetchMock.catch() twice - are you sure you want to overwrite the previous fallback response',
-			);  
+			);
 		}
 
 		this.fallbackRoute = new Route({
@@ -389,14 +320,11 @@ export default class Router {
 		});
 		this.fallbackRoute.config.isFallback = true;
 	}
-	/**
-	 *
-	 * @param {object} [options]
-	 * @param {string[]} [options.names]
-	 * @param {boolean} [options.includeSticky]
-	 * @param {boolean} [options.includeFallback]
-	 */
-	removeRoutes({ names, includeSticky, includeFallback } = {}) {
+	removeRoutes({ names, includeSticky, includeFallback }: {
+		names?: string[];
+		includeSticky?: boolean;
+		includeFallback?: boolean;
+	} = {}) {
 		includeFallback = includeFallback ?? true;
 		this.routes = this.routes.filter(({ config: { sticky, name } }) => {
 			if (sticky && !includeSticky) {
diff --git a/packages/core/src/StatusTextMap.js b/packages/core/src/StatusTextMap.ts
similarity index 95%
rename from packages/core/src/StatusTextMap.js
rename to packages/core/src/StatusTextMap.ts
index 6b33ce9..6813764 100644
--- a/packages/core/src/StatusTextMap.js
+++ b/packages/core/src/StatusTextMap.ts
@@ -1,8 +1,4 @@
-//@type-check
-/**
- * @type {Object.<number, string>}
- */
-const statusTextMap = {
+const statusTextMap: {[key: number]: string} = {
 	100: 'Continue',
 	101: 'Switching Protocols',
 	102: 'Processing',
diff --git a/packages/core/src/index.js b/packages/core/src/index.ts
similarity index 100%
rename from packages/core/src/index.js
rename to packages/core/src/index.ts
diff --git a/packages/core/types/CallHistory.d.ts b/packages/core/types/CallHistory.d.ts
deleted file mode 100644
index e3a7634..0000000
--- a/packages/core/types/CallHistory.d.ts
+++ /dev/null
@@ -1,38 +0,0 @@
-export default CallHistory;
-export type RouteConfig = import("./Route.js").RouteConfig;
-export type RouteName = import("./Route.js").RouteName;
-export type NormalizedRequestOptions = import("./RequestUtils.js").NormalizedRequestOptions;
-export type RouteMatcher = import("./Matchers.js").RouteMatcher;
-export type FetchMockConfig = import("./FetchMock.js").FetchMockConfig;
-export type CallLog = {
-    args: any[];
-    url: string;
-    options: NormalizedRequestOptions;
-    request?: Request;
-    signal?: AbortSignal;
-    route?: Route;
-    response?: Response;
-    expressParams?: {
-        [x: string]: string;
-    };
-    queryParams?: URLSearchParams;
-    pendingPromises: Promise<any>[];
-};
-export type Matched = "matched";
-export type Unmatched = "unmatched";
-export type CallHistoryFilter = RouteName | Matched | Unmatched | boolean | RouteMatcher;
-declare class CallHistory {
-    constructor(config: FetchMockConfig, router: Router);
-    callLogs: CallLog[];
-    config: import("./FetchMock.js").FetchMockConfig;
-    router: Router;
-    recordCall(callLog: CallLog): void;
-    clear(): void;
-    flush(waitForResponseMethods?: boolean): Promise<void>;
-    calls(filter: CallHistoryFilter, options: RouteConfig): CallLog[];
-    called(filter: CallHistoryFilter, options: RouteConfig): boolean;
-    lastCall(filter: CallHistoryFilter, options: RouteConfig): CallLog;
-    done(routeNames?: RouteName | RouteName[]): boolean;
-}
-import Route from './Route.js';
-import Router from './Router.js';
diff --git a/packages/core/types/FetchHandler.d.ts b/packages/core/types/FetchHandler.d.ts
deleted file mode 100644
index e69de29..0000000
diff --git a/packages/core/types/FetchMock.d.ts b/packages/core/types/FetchMock.d.ts
deleted file mode 100644
index 8d1e028..0000000
--- a/packages/core/types/FetchMock.d.ts
+++ /dev/null
@@ -1,107 +0,0 @@
-export class FetchMock {
-    constructor(config: FetchMockConfig, router?: Router);
-    config: FetchMockConfig;
-    router: Router;
-    callHistory: CallHistory;
-    createInstance(): FetchMock;
-    fetchHandler(this: FetchMock, requestInput: string | URL | Request, requestInit?: RequestInit): Promise<Response>;
-    route(matcher: UserRouteConfig): FetchMock;
-    route(matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    catch(this: FetchMock, response?: RouteResponse): FetchMock;
-    defineMatcher(matcher: MatcherDefinition): void;
-    removeRoutes(options?: {
-        names?: string[];
-        includeSticky?: boolean;
-        includeFallback?: boolean;
-    }): this;
-    clearHistory(this: FetchMock): FetchMock;
-    sticky: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    once: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    any: (this: FetchMock, response: RouteResponse, options?: UserRouteConfig | string) => FetchMock;
-    anyOnce: (this: FetchMock, response: RouteResponse, options?: UserRouteConfig | string) => FetchMock;
-    get: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    getOnce: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    post: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    postOnce: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    put: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    putOnce: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    delete: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    deleteOnce: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    head: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    headOnce: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    patch: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-    patchOnce: {
-        (this: FetchMock, matcher: UserRouteConfig): FetchMock;
-        (this: FetchMock, matcher: RouteMatcher, response: RouteResponse, options?: UserRouteConfig | string): FetchMock;
-    };
-}
-export default fetchMock;
-export type RouteMatcher = import("./Router.js").RouteMatcher;
-export type RouteName = import("./Route.js").RouteName;
-export type UserRouteConfig = import("./Route.js").UserRouteConfig;
-export type RouteResponse = import("./Router.js").RouteResponse;
-export type MatcherDefinition = import("./Matchers.js").MatcherDefinition;
-export type CallLog = import("./CallHistory.js").CallLog;
-export type RouteResponseFunction = import("./Route.js").RouteResponseFunction;
-export type FetchMockGlobalConfig = {
-    sendAsJson?: boolean;
-    includeContentLength?: boolean;
-    matchPartialBody?: boolean;
-};
-export type FetchImplementations = {
-    fetch?: typeof fetch;
-    Headers?: typeof Headers;
-    Request?: typeof Request;
-    Response?: typeof Response;
-};
-export type FetchMockConfig = FetchMockGlobalConfig & FetchImplementations;
-import Router from './Router.js';
-import CallHistory from './CallHistory.js';
-declare const fetchMock: FetchMockStandalone;
-declare class FetchMockStandalone extends FetchMock {
-    mockGlobal(this: FetchMockStandalone): FetchMockStandalone;
-    unmockGlobal(this: FetchMockStandalone): FetchMockStandalone;
-    spy(this: FetchMockStandalone, matcher?: RouteMatcher | UserRouteConfig, name?: RouteName): FetchMockStandalone;
-    spyGlobal(this: FetchMockStandalone): FetchMockStandalone;
-    createInstance(): FetchMockStandalone;
-    #private;
-}
diff --git a/packages/core/types/InstanceManagement.d.ts b/packages/core/types/InstanceManagement.d.ts
deleted file mode 100644
index 22c63e1..0000000
--- a/packages/core/types/InstanceManagement.d.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-type RequestConstructor = new (input: string | Request, init ?: RequestInit) => Request;
-
-declare type FetchMockCore ={
-    createInstance: () => FetchMock
-    config: FetchMockConfig;
-}
-
-declare type FetchMock = FetchMockCore & Router
-
-// 5. Declaration merging
-// Unlike a type alias, an interface can be defined multiple times, and will be treated as a single interface(with members of all declarations being merged).
-
-// // These two declarations become:
-// // interface Point { x: number; y: number; }
-// interface Point { x: number; }
-// interface Point { y: number; }
-
-// const point: Point = { x: 1, y: 2 };
\ No newline at end of file
diff --git a/packages/core/types/Matchers.d.ts b/packages/core/types/Matchers.d.ts
deleted file mode 100644
index ac395ef..0000000
--- a/packages/core/types/Matchers.d.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-export function isUrlMatcher(matcher: RouteMatcher | RouteConfig): matcher is RouteMatcherUrl;
-export function isFunctionMatcher(matcher: RouteMatcher | RouteConfig): matcher is RouteMatcherFunction;
-export const builtInMatchers: MatcherDefinition[];
-export type RouteConfig = import("./Route.js").RouteConfig;
-export type CallLog = import("./CallHistory.js").CallLog;
-export type URLMatcherObject = {
-    begin?: string;
-    end?: string;
-    glob?: string;
-    express?: string;
-    path?: string;
-    regexp?: RegExp;
-};
-export type RouteMatcherUrl = string | RegExp | URL | URLMatcherObject;
-export type UrlMatcherGenerator = (arg0: string) => RouteMatcherFunction;
-export type RouteMatcherFunction = (arg0: CallLog) => boolean;
-export type MatcherGenerator = (arg0: RouteConfig) => RouteMatcherFunction;
-export type RouteMatcher = RouteMatcherUrl | RouteMatcherFunction;
-export type MatcherDefinition = {
-    name: string;
-    matcher: MatcherGenerator;
-    usesBody?: boolean;
-};
diff --git a/packages/core/types/RequestUtils.d.ts b/packages/core/types/RequestUtils.d.ts
deleted file mode 100644
index 20560f1..0000000
--- a/packages/core/types/RequestUtils.d.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-export function normalizeUrl(url: string | string | URL): string;
-export function createCallLogFromUrlAndOptions(url: string | object, options: RequestInit): CallLog;
-export function createCallLogFromRequest(request: Request, options: RequestInit): Promise<CallLog>;
-export function getPath(url: string): string;
-export function getQuery(url: string): string;
-export function normalizeHeaders(headers: HeadersInit | {
-    [x: string]: string | number;
-}): {
-    [x: string]: string;
-};
-export type DerivedRequestOptions = {
-    method: string;
-    body?: string;
-    headers?: {
-        [key: string]: string;
-    };
-};
-export type NormalizedRequestOptions = RequestInit | (RequestInit & DerivedRequestOptions);
-export type CallLog = import("./CallHistory.js").CallLog;
diff --git a/packages/core/types/Route.d.ts b/packages/core/types/Route.d.ts
deleted file mode 100644
index 6f3d25e..0000000
--- a/packages/core/types/Route.d.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-export default Route;
-export type RouteMatcher = import("./Matchers.js").RouteMatcher;
-export type CallLog = import("./CallHistory.js").CallLog;
-export type RouteMatcherFunction = import("./Matchers.js").RouteMatcherFunction;
-export type RouteMatcherUrl = import("./Matchers.js").RouteMatcherUrl;
-export type MatcherDefinition = import("./Matchers.js").MatcherDefinition;
-export type FetchMockGlobalConfig = import("./FetchMock.js").FetchMockGlobalConfig;
-export type FetchImplementations = import("./FetchMock.js").FetchImplementations;
-export type UserRouteSpecificConfig = {
-    name?: RouteName;
-    method?: string;
-    headers?: {
-        [key: string]: string | number;
-    };
-    missingHeaders?: string[];
-    query?: {
-        [key: string]: string;
-    };
-    params?: {
-        [key: string]: string;
-    };
-    body?: object;
-    matcherFunction?: RouteMatcherFunction;
-    url?: RouteMatcherUrl;
-    response?: RouteResponse | RouteResponseFunction;
-    repeat?: number;
-    delay?: number;
-    sticky?: boolean;
-};
-export type InternalRouteConfig = {
-    usesBody?: boolean;
-    isFallback?: boolean;
-};
-export type UserRouteConfig = UserRouteSpecificConfig & FetchMockGlobalConfig;
-export type RouteConfig = UserRouteConfig & FetchImplementations & InternalRouteConfig;
-export type RouteResponseConfig = {
-    body?: string | {};
-    status?: number;
-    headers?: {
-        [key: string]: string;
-    };
-    throws?: Error;
-    redirectUrl?: string;
-    options?: ResponseInit;
-};
-export type ResponseInitUsingHeaders = {
-    status: number;
-    statusText: string;
-    headers: Headers;
-};
-export type RouteResponseObjectData = RouteResponseConfig | object;
-export type RouteResponseData = Response | number | string | RouteResponseObjectData;
-export type RouteResponsePromise = Promise<RouteResponseData>;
-export type RouteResponseFunction = (arg0: CallLog) => (RouteResponseData | RouteResponsePromise);
-export type RouteResponse = RouteResponseData | RouteResponsePromise | RouteResponseFunction;
-export type RouteName = string;
-declare class Route {
-    static defineMatcher(matcher: MatcherDefinition): void;
-    static registeredMatchers: MatcherDefinition[];
-    constructor(config: RouteConfig);
-    config: RouteConfig;
-    matcher: RouteMatcherFunction | undefined;
-    reset(): void;
-    constructResponse(responseInput: RouteResponseConfig): {
-        response: Response;
-        responseOptions: ResponseInit;
-        responseInput: RouteResponseConfig;
-    };
-    constructResponseOptions(responseInput: RouteResponseConfig): ResponseInitUsingHeaders;
-    constructResponseBody(responseInput: RouteResponseConfig, responseOptions: ResponseInitUsingHeaders): string | null;
-    #private;
-}
diff --git a/packages/core/types/Router.d.ts b/packages/core/types/Router.d.ts
deleted file mode 100644
index 56a6bc7..0000000
--- a/packages/core/types/Router.d.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-export default class Router {
-    constructor(fetchMockConfig: FetchMockConfig, { routes, fallbackRoute }?: {
-        routes?: Route[];
-        fallbackRoute?: Route;
-    });
-    routes: Route[];
-    config: import("./FetchMock.js").FetchMockConfig;
-    fallbackRoute: Route;
-    needsToReadBody(request: Request): boolean;
-    execute(callLog: CallLog): Promise<Response>;
-    generateResponse(callLog: CallLog): Promise<{
-        response: Response;
-        responseOptions: ResponseInit;
-        responseInput: RouteResponseConfig;
-    }>;
-    createObservableResponse(response: Response, responseConfig: ResponseInit, responseInput: RouteResponseConfig, responseUrl: string, pendingPromises: Promise<any>[]): Response;
-    addRoute(matcher: RouteMatcher | UserRouteConfig, response?: RouteResponse, nameOrOptions?: UserRouteConfig | string): void;
-    setFallback(response?: RouteResponse): void;
-    removeRoutes({ names, includeSticky, includeFallback }?: {
-        names?: string[];
-        includeSticky?: boolean;
-        includeFallback?: boolean;
-    }): void;
-}
-export type UserRouteConfig = import("./Route.js").UserRouteConfig;
-export type RouteConfig = import("./Route.js").RouteConfig;
-export type RouteResponse = import("./Route.js").RouteResponse;
-export type RouteResponseData = import("./Route.js").RouteResponseData;
-export type RouteResponseObjectData = import("./Route.js").RouteResponseObjectData;
-export type RouteResponseConfig = import("./Route.js").RouteResponseConfig;
-export type RouteResponseFunction = import("./Route.js").RouteResponseFunction;
-export type RouteMatcher = import("./Matchers.js").RouteMatcher;
-export type FetchMockConfig = import("./FetchMock.js").FetchMockConfig;
-export type FetchMock = typeof import("./FetchMock.js");
-export type CallLog = import("./CallHistory.js").CallLog;
-export type ResponseConfigProp = "body" | "headers" | "throws" | "status" | "redirectUrl";
-import Route from './Route.js';
diff --git a/packages/core/types/StatusTextMap.d.ts b/packages/core/types/StatusTextMap.d.ts
deleted file mode 100644
index eb8c81c..0000000
--- a/packages/core/types/StatusTextMap.d.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default statusTextMap;
-declare const statusTextMap: {
-    [x: number]: string;
-};
diff --git a/packages/core/types/index.d.ts b/packages/core/types/index.d.ts
deleted file mode 100644
index 79c8611..0000000
--- a/packages/core/types/index.d.ts
+++ /dev/null
@@ -1,4 +0,0 @@
-export default fetchMock;
-export { FetchMock };
-import fetchMock from './FetchMock.js';
-import { FetchMock } from './FetchMock.js';
diff --git a/tslint.json b/tslint.json
deleted file mode 100644
index c17ac4d..0000000
--- a/tslint.json
+++ /dev/null
@@ -1,1 +0,0 @@
-{ "extends": "dtslint/dtslint.json" }


Test patch:
diff --git a/packages/core/src/__tests__/FetchMock/mock-and-spy.test.js b/packages/core/src/__tests__/FetchMock/mock-and-spy.test.js
index 06ac174..03af666 100644
--- a/packages/core/src/__tests__/FetchMock/mock-and-spy.test.js
+++ b/packages/core/src/__tests__/FetchMock/mock-and-spy.test.js
@@ -1,5 +1,5 @@
 import { beforeEach, afterEach, describe, expect, it, vi } from 'vitest';
-import fetchMock from '../../FetchMock.js';
+import fetchMock from '../../FetchMock';
 
 describe('mock and spy', () => {
 	let fm;
@@ -32,7 +32,7 @@ describe('mock and spy', () => {
 			fm.mockGlobal();
 			try {
 				await fetch('http://a.com', { method: 'post' });
-			} catch (err) {}
+			} catch {} //eslint-disable-line no-empty
 			// cannot just check globalThis.fetch === fm.fetchHandler because we apply .bind() to fetchHandler
 			expect(fm.fetchHandler).toHaveBeenCalledWith('http://a.com', {
 				method: 'post',
@@ -44,7 +44,7 @@ describe('mock and spy', () => {
 			const response = await fetch('http://a.com', { method: 'post' });
 			expect(response.status).toEqual(200);
 			const callLog = fm.callHistory.lastCall();
-			expect(callLog.args).toEqual(['http://a.com/', { method: 'post' }]);
+			expect(callLog.args).toEqual(['http://a.com', { method: 'post' }]);
 		});
 
 		it('restores global fetch', () => {
@@ -60,7 +60,7 @@ describe('mock and spy', () => {
 			fm.spy();
 			try {
 				await fm.fetchHandler('http://a.com/', { method: 'post' });
-			} catch (err) {}
+			} catch {} //eslint-disable-line no-empty
 			expect(fm.config.fetch).toHaveBeenCalledWith('http://a.com/', {
 				method: 'post',
 			});
@@ -72,7 +72,7 @@ describe('mock and spy', () => {
 			try {
 				await fm.fetchHandler('http://a.com/', { method: 'post' });
 				await fm.fetchHandler('http://b.com/', { method: 'post' });
-			} catch (err) {}
+			} catch {} //eslint-disable-line no-empty
 
 			expect(fm.config.fetch).toHaveBeenCalledTimes(1);
 			expect(fm.config.fetch).toHaveBeenCalledWith('http://a.com/', {
@@ -90,7 +90,7 @@ describe('mock and spy', () => {
 					method: 'delete',
 					headers: { check: 'this' },
 				});
-			} catch (err) {}
+			} catch {} //eslint-disable-line no-empty
 			expect(fm.config.fetch).toHaveBeenCalledTimes(1);
 			expect(fm.config.fetch).toHaveBeenCalledWith('http://a.com/', {
 				method: 'delete',
@@ -103,7 +103,7 @@ describe('mock and spy', () => {
 			fm.spy('http://a.com/', 'myroute').catch();
 			try {
 				await fm.fetchHandler('http://a.com/');
-			} catch (err) {}
+			} catch {} //eslint-disable-line no-empty
 			expect(fm.callHistory.called('myroute')).toBe(true);
 		});
 
@@ -112,7 +112,7 @@ describe('mock and spy', () => {
 			fm.mockGlobal().spy('http://a.com', 200);
 			try {
 				await fm.fetchHandler('http://a.com/', { method: 'post' });
-			} catch (err) {}
+			} catch {} //eslint-disable-line no-empty
 			expect(fm.config.fetch).toHaveBeenCalledTimes(1);
 			expect(fm.config.fetch).toHaveBeenCalledWith('http://a.com/', {
 				method: 'post',
@@ -124,7 +124,7 @@ describe('mock and spy', () => {
 			fm.spyGlobal();
 			try {
 				await fm.fetchHandler('http://a.com/', { method: 'post' });
-			} catch (err) {}
+			} catch {} //eslint-disable-line no-empty
 			expect(fm.config.fetch).toHaveBeenCalledTimes(1);
 			expect(fm.config.fetch).toHaveBeenCalledWith('http://a.com/', {
 				method: 'post',
diff --git a/packages/core/src/__tests__/FetchMock/routing.test.js b/packages/core/src/__tests__/FetchMock/routing.test.js
index 17f7eb0..450799d 100644
--- a/packages/core/src/__tests__/FetchMock/routing.test.js
+++ b/packages/core/src/__tests__/FetchMock/routing.test.js
@@ -1,5 +1,5 @@
 import { beforeEach, describe, expect, it, vi } from 'vitest';
-import fetchMock from '../../FetchMock.js';
+import fetchMock from '../../FetchMock';
 
 describe('Routing', () => {
 	let fm;
@@ -83,7 +83,7 @@ describe('Routing', () => {
 				try {
 					await fm.fetchHandler('http://a.com/');
 					expect.unreachable('Previous line should throw');
-				} catch (err) {}
+				} catch {} // eslint-disable-line no-empty
 			});
 		});
 
diff --git a/packages/core/src/__tests__/Matchers/body.test.js b/packages/core/src/__tests__/Matchers/body.test.js
index 26d80e0..0bab250 100644
--- a/packages/core/src/__tests__/Matchers/body.test.js
+++ b/packages/core/src/__tests__/Matchers/body.test.js
@@ -1,7 +1,7 @@
 import { describe, expect, it } from 'vitest';
-import Route from '../../Route.js';
-import Router from '../../Router.js';
-import { createCallLogFromRequest } from '../../RequestUtils.js';
+import Route from '../../Route';
+import Router from '../../Router';
+import { createCallLogFromRequest } from '../../RequestUtils';
 describe('body matching', () => {
 	it('should not match if no body provided in request', () => {
 		const route = new Route({ body: { foo: 'bar' }, response: 200 });
diff --git a/packages/core/src/__tests__/Matchers/express.test.js b/packages/core/src/__tests__/Matchers/express.test.js
index 27d31e9..44e2946 100644
--- a/packages/core/src/__tests__/Matchers/express.test.js
+++ b/packages/core/src/__tests__/Matchers/express.test.js
@@ -1,5 +1,5 @@
 import { describe, expect, it } from 'vitest';
-import Route from '../../Route.js';
+import Route from '../../Route';
 
 describe('express path parameter matching', () => {
 	it('can match a path parameters', () => {
diff --git a/packages/core/src/__tests__/Matchers/function.test.js b/packages/core/src/__tests__/Matchers/function.test.js
index 7efc066..cdc5e71 100644
--- a/packages/core/src/__tests__/Matchers/function.test.js
+++ b/packages/core/src/__tests__/Matchers/function.test.js
@@ -1,5 +1,5 @@
 import { describe, expect, it } from 'vitest';
-import Route from '../../Route.js';
+import Route from '../../Route';
 
 describe('function matching', () => {
 	it('match using custom function', () => {
diff --git a/packages/core/src/__tests__/Matchers/headers.test.js b/packages/core/src/__tests__/Matchers/headers.test.js
index c0335ca..1f7f0f0 100644
--- a/packages/core/src/__tests__/Matchers/headers.test.js
+++ b/packages/core/src/__tests__/Matchers/headers.test.js
@@ -1,5 +1,5 @@
 import { describe, expect, it } from 'vitest';
-import Route from '../../Route.js';
+import Route from '../../Route';
 
 describe('header matching', () => {
 	it('not match when headers not present', () => {
diff --git a/packages/core/src/__tests__/Matchers/method.test.js b/packages/core/src/__tests__/Matchers/method.test.js
index d30c820..5d19801 100644
--- a/packages/core/src/__tests__/Matchers/method.test.js
+++ b/packages/core/src/__tests__/Matchers/method.test.js
@@ -1,6 +1,6 @@
 import { describe, expect, it } from 'vitest';
 
-import Route from '../../Route.js';
+import Route from '../../Route';
 
 describe('method matching', () => {
 	it('match any method by default', () => {
diff --git a/packages/core/src/__tests__/Matchers/query-string.test.js b/packages/core/src/__tests__/Matchers/query-string.test.js
index 3587771..498b7c4 100644
--- a/packages/core/src/__tests__/Matchers/query-string.test.js
+++ b/packages/core/src/__tests__/Matchers/query-string.test.js
@@ -1,5 +1,5 @@
 import { describe, expect, it } from 'vitest';
-import Route from '../../Route.js';
+import Route from '../../Route';
 
 describe('query string matching', () => {
 	it('match a query string', () => {
diff --git a/packages/core/src/__tests__/Matchers/route-config-object.test.js b/packages/core/src/__tests__/Matchers/route-config-object.test.js
index 4d461fc..59c046d 100644
--- a/packages/core/src/__tests__/Matchers/route-config-object.test.js
+++ b/packages/core/src/__tests__/Matchers/route-config-object.test.js
@@ -1,5 +1,5 @@
 import { describe, expect, it } from 'vitest';
-import Route from '../../Route.js';
+import Route from '../../Route';
 
 // TODO should this whole thing be integration tests on router
 // as it's mainly about the shape of optiosn passed into to addRoute
diff --git a/packages/core/src/__tests__/Matchers/url.test.js b/packages/core/src/__tests__/Matchers/url.test.js
index 5c14014..9e29e80 100644
--- a/packages/core/src/__tests__/Matchers/url.test.js
+++ b/packages/core/src/__tests__/Matchers/url.test.js
@@ -1,5 +1,5 @@
 import { describe, expect, it } from 'vitest';
-import Route from '../../Route.js';
+import Route from '../../Route';
 
 describe('url matching', () => {
 	it('match exact strings', () => {
@@ -87,7 +87,7 @@ describe('url matching', () => {
 	});
 
 	it('match regular expressions', () => {
-		const rx = /http\:\/\/a\.com\/\d+/;
+		const rx = /http:\/\/a\.com\/\d+/;
 		const route = new Route({ url: rx, response: 200 });
 
 		expect(route.matcher({ url: 'http://a.com/' })).toBe(false);
@@ -193,7 +193,7 @@ describe('url matching', () => {
 			expect(route.matcher({ url: 'data:text/plain,path' })).toBe(true);
 		});
 		it('match regular expressions', () => {
-			const rx = /data\:text\/plain,\d+/;
+			const rx = /data:text\/plain,\d+/;
 			const route = new Route({ url: rx, response: 200 });
 			expect(route.matcher({ url: 'data:text/html,12345' })).toBe(false);
 			expect(route.matcher({ url: 'data:text/plain,path' })).toBe(false);