Repository: apollographql/apollo-client

Problem statement:
Enable `strict` setting in tsconfig.json
### Issue Description

Our codebase does not currently use the `strict` setting for our codebase. Enabling this causes our codebase to fail due to the [`strictFunctionTypes` rule](https://www.typescriptlang.org/tsconfig#strictFunctionTypes).

As a best practice, we should be using `strict` to avoid subtle bugs that may arise without this enabled.

### Link to Reproduction

The codebase

### Reproduction Steps

Add `strict: true` to `tsconfig.json` and watch all the tests fail due to TS issues (primarily with `src/utilities/graphql/transform.ts`). 


Hints:

Patch:
diff --git a/.size-limit.cjs b/.size-limit.cjs
--- a/.size-limit.cjs
+++ b/.size-limit.cjs
@@ -1,7 +1,7 @@
 const checks = [
   {
     path: "dist/apollo-client.min.cjs",
-    limit: "37972",
+    limit: "37975",
   },
   {
     path: "dist/main.cjs",
@@ -10,7 +10,7 @@ const checks = [
   {
     path: "dist/index.js",
     import: "{ ApolloClient, InMemoryCache, HttpLink }",
-    limit: "32017",
+    limit: "32019",
   },
   ...[
     "ApolloProvider",
diff --git a/src/cache/core/types/Cache.ts b/src/cache/core/types/Cache.ts
--- a/src/cache/core/types/Cache.ts
+++ b/src/cache/core/types/Cache.ts
@@ -93,7 +93,7 @@ export namespace Cache {
       this: TCache,
       watch: Cache.WatchOptions,
       diff: Cache.DiffResult<any>,
-      lastDiff: Cache.DiffResult<any> | undefined
+      lastDiff?: Cache.DiffResult<any> | undefined
     ) => any;
   }
 
diff --git a/src/cache/inmemory/entityStore.ts b/src/cache/inmemory/entityStore.ts
--- a/src/cache/inmemory/entityStore.ts
+++ b/src/cache/inmemory/entityStore.ts
@@ -593,7 +593,7 @@ class CacheGroup {
 
   // Used by the EntityStore#makeCacheKey method to compute cache keys
   // specific to this CacheGroup.
-  public keyMaker: Trie<object>;
+  public keyMaker!: Trie<object>;
 
   constructor(
     public readonly caching: boolean,
@@ -800,7 +800,11 @@ class Layer extends EntityStore {
   public getStorage(): StorageType {
     let p: EntityStore = this.parent;
     while ((p as Layer).parent) p = (p as Layer).parent;
-    return p.getStorage.apply(p, arguments);
+    return p.getStorage.apply(
+      p,
+      // @ts-expect-error
+      arguments
+    );
   }
 }
 
@@ -823,20 +827,20 @@ class Stump extends Layer {
     return this;
   }
 
-  public merge() {
+  public merge(older: string | StoreObject, newer: string | StoreObject) {
     // We never want to write any data into the Stump, so we forward any merge
     // calls to the Root instead. Another option here would be to throw an
     // exception, but the toReference(object, true) function can sometimes
     // trigger Stump writes (which used to be Root writes, before the Stump
     // concept was introduced).
-    return this.parent.merge.apply(this.parent, arguments);
+    return this.parent.merge(older, newer);
   }
 }
 
 function storeObjectReconciler(
   existingObject: StoreObject,
   incomingObject: StoreObject,
-  property: string
+  property: string | number
 ): StoreValue {
   const existingValue = existingObject[property];
   const incomingValue = incomingObject[property];
diff --git a/src/cache/inmemory/fixPolyfills.native.ts b/src/cache/inmemory/fixPolyfills.native.ts
--- a/src/cache/inmemory/fixPolyfills.native.ts
+++ b/src/cache/inmemory/fixPolyfills.native.ts
@@ -33,10 +33,10 @@ try {
   // https://github.com/apollographql/react-apollo/issues/2442#issuecomment-426489517
   testMap.set(frozen, frozen).delete(frozen);
 } catch {
-  const wrap = (method: <T>(obj: T) => T): typeof method => {
+  const wrap = <M extends <T>(obj: T) => T>(method: M): M => {
     return (
       method &&
-      ((obj) => {
+      (((obj) => {
         try {
           // If .set succeeds, also call .delete to avoid leaking memory.
           testMap.set(obj, obj).delete(obj);
@@ -45,7 +45,7 @@ try {
           // by this return-from-finally statement:
           return method.call(Object, obj);
         }
-      })
+      }) as M)
     );
   };
   Object.freeze = wrap(Object.freeze);
diff --git a/src/cache/inmemory/fragmentRegistry.ts b/src/cache/inmemory/fragmentRegistry.ts
--- a/src/cache/inmemory/fragmentRegistry.ts
+++ b/src/cache/inmemory/fragmentRegistry.ts
@@ -6,6 +6,7 @@ import type {
 } from "graphql";
 import { visit } from "graphql";
 
+import type { OptimisticWrapperFunction } from "optimism";
 import { wrap } from "optimism";
 
 import type { FragmentMap } from "../../utilities/index.js";
@@ -29,24 +30,22 @@ export function createFragmentRegistry(
   return new FragmentRegistry(...fragments);
 }
 
-const { forEach: arrayLikeForEach } = Array.prototype;
-
 class FragmentRegistry implements FragmentRegistryAPI {
   private registry: FragmentMap = Object.create(null);
 
-  // Call static method FragmentRegistry.from(...) instead of invoking the
+  // Call `createFragmentRegistry` instead of invoking the
   // FragmentRegistry constructor directly. This reserves the constructor for
   // future configuration of the FragmentRegistry.
   constructor(...fragments: DocumentNode[]) {
     this.resetCaches();
     if (fragments.length) {
-      this.register.apply(this, fragments);
+      this.register(...fragments);
     }
   }
 
-  public register(): this {
+  public register(...fragments: DocumentNode[]): this {
     const definitions = new Map<string, FragmentDefinitionNode>();
-    arrayLikeForEach.call(arguments, (doc: DocumentNode) => {
+    fragments.forEach((doc: DocumentNode) => {
       getFragmentDefinitions(doc).forEach((node) => {
         definitions.set(node.name.value, node);
       });
@@ -66,27 +65,15 @@ class FragmentRegistry implements FragmentRegistryAPI {
   private invalidate(name: string) {}
 
   public resetCaches() {
-    this.invalidate = (this.lookup = this.cacheUnaryMethod("lookup")).dirty; // This dirty function is bound to the wrapped lookup method.
-    this.transform = this.cacheUnaryMethod("transform");
-    this.findFragmentSpreads = this.cacheUnaryMethod("findFragmentSpreads");
+    this.invalidate = (this.lookup = this.cacheUnaryMethod(this.lookup)).dirty; // This dirty function is bound to the wrapped lookup method.
+    this.transform = this.cacheUnaryMethod(this.transform);
+    this.findFragmentSpreads = this.cacheUnaryMethod(this.findFragmentSpreads);
   }
 
-  private cacheUnaryMethod<
-    TName extends keyof Pick<
-      FragmentRegistry,
-      "lookup" | "transform" | "findFragmentSpreads"
-    >,
-  >(name: TName) {
-    const registry = this;
-    const originalMethod = FragmentRegistry.prototype[name];
-    return wrap(
-      function () {
-        return originalMethod.apply(registry, arguments);
-      },
-      {
-        makeCacheKey: (arg) => arg,
-      }
-    );
+  private cacheUnaryMethod<F extends (arg: any) => any>(originalMethod: F) {
+    return wrap<Parameters<F>, ReturnType<F>>(originalMethod.bind(this), {
+      makeCacheKey: (arg) => arg,
+    }) as OptimisticWrapperFunction<Parameters<F>, ReturnType<F>> & F;
   }
 
   public lookup(fragmentName: string): FragmentDefinitionNode | null {
diff --git a/src/cache/inmemory/inMemoryCache.ts b/src/cache/inmemory/inMemoryCache.ts
--- a/src/cache/inmemory/inMemoryCache.ts
+++ b/src/cache/inmemory/inMemoryCache.ts
@@ -33,18 +33,18 @@ type BroadcastOptions = Pick<
 >;
 
 export class InMemoryCache extends ApolloCache<NormalizedCacheObject> {
-  private data: EntityStore;
-  private optimisticData: EntityStore;
+  private data!: EntityStore;
+  private optimisticData!: EntityStore;
 
   protected config: InMemoryCacheConfig;
   private watches = new Set<Cache.WatchOptions>();
   private addTypename: boolean;
 
-  private storeReader: StoreReader;
-  private storeWriter: StoreWriter;
+  private storeReader!: StoreReader;
+  private storeWriter!: StoreWriter;
   private addTypenameTransform = new DocumentTransform(addTypenameToDocument);
 
-  private maybeBroadcastWatch: OptimisticWrapperFunction<
+  private maybeBroadcastWatch!: OptimisticWrapperFunction<
     [Cache.WatchOptions, BroadcastOptions?],
     any,
     [Cache.WatchOptions]
diff --git a/src/cache/inmemory/readFromStore.ts b/src/cache/inmemory/readFromStore.ts
--- a/src/cache/inmemory/readFromStore.ts
+++ b/src/cache/inmemory/readFromStore.ts
@@ -535,7 +535,7 @@ function firstMissing(tree: MissingTree): string | undefined {
       return value;
     });
   } catch (result) {
-    return result;
+    return result as string;
   }
 }
 
diff --git a/src/core/ApolloClient.ts b/src/core/ApolloClient.ts
--- a/src/core/ApolloClient.ts
+++ b/src/core/ApolloClient.ts
@@ -86,7 +86,7 @@ export class ApolloClient<TCacheShape> implements DataProxy {
   public readonly typeDefs: ApolloClientOptions<TCacheShape>["typeDefs"];
 
   private queryManager: QueryManager<TCacheShape>;
-  private devToolsHookCb: Function;
+  private devToolsHookCb?: Function;
   private resetStoreCallbacks: Array<() => Promise<any>> = [];
   private clearStoreCallbacks: Array<() => Promise<any>> = [];
   private localState: LocalState<TCacheShape>;
@@ -592,7 +592,9 @@ export class ApolloClient<TCacheShape> implements DataProxy {
   >(
     options: RefetchQueriesOptions<TCache, TResult>
   ): RefetchQueriesResult<TResult> {
-    const map = this.queryManager.refetchQueries(options);
+    const map = this.queryManager.refetchQueries(
+      options as RefetchQueriesOptions<ApolloCache<TCacheShape>, TResult>
+    );
     const queries: ObservableQuery<any>[] = [];
     const results: InternalRefetchQueriesResult<TResult>[] = [];
 
diff --git a/src/core/LocalState.ts b/src/core/LocalState.ts
--- a/src/core/LocalState.ts
+++ b/src/core/LocalState.ts
@@ -75,9 +75,9 @@ export type LocalStateOptions<TCacheShape> = {
 
 export class LocalState<TCacheShape> {
   private cache: ApolloCache<TCacheShape>;
-  private client: ApolloClient<TCacheShape>;
+  private client?: ApolloClient<TCacheShape>;
   private resolvers?: Resolvers;
-  private fragmentMatcher: FragmentMatcher;
+  private fragmentMatcher?: FragmentMatcher;
   private selectionsToResolveCache = new WeakMap<
     ExecutableDefinitionNode,
     Set<SelectionNode>
@@ -162,7 +162,7 @@ export class LocalState<TCacheShape> {
     this.fragmentMatcher = fragmentMatcher;
   }
 
-  public getFragmentMatcher(): FragmentMatcher {
+  public getFragmentMatcher(): FragmentMatcher | undefined {
     return this.fragmentMatcher;
   }
 
@@ -197,11 +197,11 @@ export class LocalState<TCacheShape> {
   // To support `@client @export(as: "someVar")` syntax, we'll first resolve
   // @client @export fields locally, then pass the resolved values back to be
   // used alongside the original operation variables.
-  public async addExportedVariables(
+  public async addExportedVariables<TVars extends OperationVariables>(
     document: DocumentNode,
-    variables: OperationVariables = {},
+    variables: TVars = {} as TVars,
     context = {}
-  ) {
+  ): /* returns at least the variables that were passed in */ Promise<TVars> {
     if (document) {
       return this.resolveDocument(
         document,
diff --git a/src/core/ObservableQuery.ts b/src/core/ObservableQuery.ts
--- a/src/core/ObservableQuery.ts
+++ b/src/core/ObservableQuery.ts
@@ -35,6 +35,7 @@ import type { QueryInfo } from "./QueryInfo.js";
 import type { MissingFieldError } from "../cache/index.js";
 import type { MissingTree } from "../cache/core/types/common.js";
 import { equalByQuery } from "./equalByQuery.js";
+import type { TODO } from "../utilities/types/TODO.js";
 
 const { assign, hasOwnProperty } = Object;
 
@@ -924,8 +925,9 @@ Did you mean to call refetch(variables) instead of refetch({ variables })?`,
   public reobserve(
     newOptions?: Partial<WatchQueryOptions<TVariables, TData>>,
     newNetworkStatus?: NetworkStatus
-  ) {
-    return this.reobserveAsConcast(newOptions, newNetworkStatus).promise;
+  ): Promise<ApolloQueryResult<TData>> {
+    return this.reobserveAsConcast(newOptions, newNetworkStatus)
+      .promise as TODO;
   }
 
   public resubscribeAfterError(
@@ -1048,14 +1050,18 @@ export function reobserveCacheFirst<TData, TVars extends OperationVariables>(
       fetchPolicy: "cache-first",
       // Use a temporary nextFetchPolicy function that replaces itself with the
       // previous nextFetchPolicy value and returns the original fetchPolicy.
-      nextFetchPolicy(this: WatchQueryOptions<TVars, TData>) {
+      nextFetchPolicy(
+        this: WatchQueryOptions<TVars, TData>,
+        currentFetchPolicy: WatchQueryFetchPolicy,
+        context: NextFetchPolicyContext<TData, TVars>
+      ) {
         // Replace this nextFetchPolicy function in the options object with the
         // original this.options.nextFetchPolicy value.
         this.nextFetchPolicy = nextFetchPolicy;
         // If the original nextFetchPolicy value was a function, give it a
         // chance to decide what happens here.
-        if (typeof nextFetchPolicy === "function") {
-          return nextFetchPolicy.apply(this, arguments);
+        if (typeof this.nextFetchPolicy === "function") {
+          return this.nextFetchPolicy(currentFetchPolicy, context);
         }
         // Otherwise go back to the original this.options.fetchPolicy.
         return fetchPolicy!;
diff --git a/src/core/QueryInfo.ts b/src/core/QueryInfo.ts
--- a/src/core/QueryInfo.ts
+++ b/src/core/QueryInfo.ts
@@ -49,6 +49,7 @@ function wrapDestructiveCacheMethod(
         // that matters in any conceivable practical scenario.
         (destructiveMethodCounts.get(cache)! + 1) % 1e15
       );
+      // @ts-expect-error this is just too generic to be typed correctly
       return original.apply(this, arguments);
     };
   }
@@ -111,7 +112,7 @@ export class QueryInfo {
     // NetworkStatus.loading, but also possibly fetchMore, poll, refetch,
     // or setVariables.
     networkStatus?: NetworkStatus;
-    observableQuery?: ObservableQuery<any>;
+    observableQuery?: ObservableQuery<any, any>;
     lastRequestId?: number;
   }): this {
     let networkStatus = query.networkStatus || NetworkStatus.loading;
@@ -212,10 +213,10 @@ export class QueryInfo {
     }
   }
 
-  public readonly observableQuery: ObservableQuery<any> | null = null;
+  public readonly observableQuery: ObservableQuery<any, any> | null = null;
   private oqListener?: QueryListener;
 
-  setObservableQuery(oq: ObservableQuery<any> | null) {
+  setObservableQuery(oq: ObservableQuery<any, any> | null) {
     if (oq === this.observableQuery) return;
 
     if (this.oqListener) {
diff --git a/src/core/QueryManager.ts b/src/core/QueryManager.ts
--- a/src/core/QueryManager.ts
+++ b/src/core/QueryManager.ts
@@ -74,6 +74,7 @@ import {
 import type { ApolloErrorOptions } from "../errors/index.js";
 import { PROTOCOL_ERRORS_SYMBOL } from "../errors/index.js";
 import { print } from "../utilities/index.js";
+import type { TODO } from "../utilities/types/TODO.js";
 
 const { hasOwnProperty } = Object.prototype;
 
@@ -479,7 +480,7 @@ export class QueryManager<TStore> {
       const results: any[] = [];
 
       this.refetchQueries({
-        updateCache: (cache: TCache) => {
+        updateCache: (cache) => {
           if (!skipCache) {
             cacheWrites.forEach((write) => cache.write(write));
           }
@@ -526,7 +527,7 @@ export class QueryManager<TStore> {
             // either a SingleExecutionResult or the final ExecutionPatchResult,
             // call the update function.
             if (isFinalResult) {
-              update(cache, result, {
+              update(cache as TCache, result, {
                 context: mutation.context,
                 variables: mutation.variables,
               });
@@ -617,7 +618,7 @@ export class QueryManager<TStore> {
     networkStatus?: NetworkStatus
   ): Promise<ApolloQueryResult<TData>> {
     return this.fetchConcastWithInfo(queryId, options, networkStatus).concast
-      .promise;
+      .promise as TODO;
   }
 
   public getQueryStore() {
diff --git a/src/link/error/index.ts b/src/link/error/index.ts
--- a/src/link/error/index.ts
+++ b/src/link/error/index.ts
@@ -84,7 +84,7 @@ export function onError(errorHandler: ErrorHandler): ApolloLink {
           },
         });
       } catch (e) {
-        errorHandler({ networkError: e, operation, forward });
+        errorHandler({ networkError: e as Error, operation, forward });
         observer.error(e);
       }
 
diff --git a/src/link/http/HttpLink.ts b/src/link/http/HttpLink.ts
--- a/src/link/http/HttpLink.ts
+++ b/src/link/http/HttpLink.ts
@@ -1,10 +1,8 @@
-import type { RequestHandler } from "../core/index.js";
 import { ApolloLink } from "../core/index.js";
 import type { HttpOptions } from "./selectHttpOptionsAndBody.js";
 import { createHttpLink } from "./createHttpLink.js";
 
 export class HttpLink extends ApolloLink {
-  public requester: RequestHandler;
   constructor(public options: HttpOptions = {}) {
     super(createHttpLink(options).request);
   }
diff --git a/src/link/http/iterators/reader.ts b/src/link/http/iterators/reader.ts
--- a/src/link/http/iterators/reader.ts
+++ b/src/link/http/iterators/reader.ts
@@ -6,7 +6,7 @@
 import { canUseAsyncIteratorSymbol } from "../../../utilities/index.js";
 
 interface ReaderIterator<T> {
-  next(): Promise<ReadableStreamReadResult<T>>;
+  next(): Promise<IteratorResult<T, T | undefined>>;
   [Symbol.asyncIterator]?(): AsyncIterator<T>;
 }
 
@@ -15,12 +15,20 @@ export default function readerIterator<T>(
 ): AsyncIterableIterator<T> {
   const iterator: ReaderIterator<T> = {
     next() {
-      return reader.read();
+      return reader.read() as Promise<
+        | ReadableStreamReadValueResult<T>
+        // DoneResult has `value` optional, which doesn't comply with an
+        // `IteratorResult`, so we assert it to `T | undefined` instead
+        | Required<ReadableStreamReadDoneResult<T | undefined>>
+      >;
     },
   };
 
   if (canUseAsyncIteratorSymbol) {
-    iterator[Symbol.asyncIterator] = function (): AsyncIterator<T> {
+    iterator[Symbol.asyncIterator] = function (): AsyncIterator<
+      T,
+      T | undefined
+    > {
       return this;
     };
   }
diff --git a/src/link/http/serializeFetchParameter.ts b/src/link/http/serializeFetchParameter.ts
--- a/src/link/http/serializeFetchParameter.ts
+++ b/src/link/http/serializeFetchParameter.ts
@@ -9,7 +9,7 @@ export const serializeFetchParameter = (p: any, label: string) => {
   let serialized;
   try {
     serialized = JSON.stringify(p);
-  } catch (e) {
+  } catch (e: any) {
     const parseError = newInvariantError(
       `Network request failed. %s is not serializable: %s`,
       label,
diff --git a/src/react/cache/QueryReference.ts b/src/react/cache/QueryReference.ts
--- a/src/react/cache/QueryReference.ts
+++ b/src/react/cache/QueryReference.ts
@@ -73,7 +73,7 @@ export class InternalQueryReference<TData = unknown> {
 
   private subscription: ObservableSubscription;
   private listeners = new Set<Listener<TData>>();
-  private autoDisposeTimeoutId: NodeJS.Timeout;
+  private autoDisposeTimeoutId?: NodeJS.Timeout;
   private status: "idle" | "loading" = "loading";
 
   private resolve: ((result: ApolloQueryResult<TData>) => void) | undefined;
diff --git a/src/react/cache/SuspenseCache.ts b/src/react/cache/SuspenseCache.ts
--- a/src/react/cache/SuspenseCache.ts
+++ b/src/react/cache/SuspenseCache.ts
@@ -32,7 +32,9 @@ export class SuspenseCache {
     cacheKey: CacheKey,
     createObservable: () => ObservableQuery<TData>
   ) {
-    const ref = this.queryRefs.lookupArray(cacheKey);
+    const ref = this.queryRefs.lookupArray(cacheKey) as {
+      current?: InternalQueryReference<TData>;
+    };
 
     if (!ref.current) {
       ref.current = new InternalQueryReference(createObservable(), {
@@ -44,6 +46,6 @@ export class SuspenseCache {
       });
     }
 
-    return ref.current as InternalQueryReference<TData>;
+    return ref.current;
   }
 }
diff --git a/src/react/hooks/useBackgroundQuery.ts b/src/react/hooks/useBackgroundQuery.ts
--- a/src/react/hooks/useBackgroundQuery.ts
+++ b/src/react/hooks/useBackgroundQuery.ts
@@ -1,8 +1,10 @@
 import * as React from "react";
 import type {
   DocumentNode,
+  FetchMoreQueryOptions,
   OperationVariables,
   TypedDocumentNode,
+  WatchQueryOptions,
 } from "../../core/index.js";
 import { useApolloClient } from "./useApolloClient.js";
 import { wrapQueryRef } from "../cache/QueryReference.js";
@@ -197,7 +199,7 @@ export function useBackgroundQuery<
   ];
 
   const queryRef = suspenseCache.getQueryRef(cacheKey, () =>
-    client.watchQuery(watchQueryOptions)
+    client.watchQuery(watchQueryOptions as WatchQueryOptions<any, any>)
   );
 
   const [promiseCache, setPromiseCache] = React.useState(
@@ -213,7 +215,7 @@ export function useBackgroundQuery<
 
   const fetchMore: FetchMoreFunction<TData, TVariables> = React.useCallback(
     (options) => {
-      const promise = queryRef.fetchMore(options);
+      const promise = queryRef.fetchMore(options as FetchMoreQueryOptions<any>);
 
       setPromiseCache((promiseCache) =>
         new Map(promiseCache).set(queryRef.key, queryRef.promise)
diff --git a/src/react/hooks/useLazyQuery.ts b/src/react/hooks/useLazyQuery.ts
--- a/src/react/hooks/useLazyQuery.ts
+++ b/src/react/hooks/useLazyQuery.ts
@@ -75,6 +75,7 @@ export function useLazyQuery<
           // Only the first time populating execOptionsRef.current matters here.
           internalState.forceUpdateState();
         }
+        // @ts-expect-error this is just too generic to type
         return method.apply(this, arguments);
       };
     }
diff --git a/src/react/hooks/useMutation.ts b/src/react/hooks/useMutation.ts
--- a/src/react/hooks/useMutation.ts
+++ b/src/react/hooks/useMutation.ts
@@ -12,6 +12,7 @@ import type {
 import type {
   ApolloCache,
   DefaultContext,
+  MutationOptions,
   OperationVariables,
 } from "../../core/index.js";
 import { mergeOptions } from "../../utilities/index.js";
@@ -87,10 +88,10 @@ export function useMutation<
       }
 
       const mutationId = ++ref.current.mutationId;
-      const clientOptions = mergeOptions(baseOptions, executeOptions as any);
+      const clientOptions = mergeOptions(baseOptions, executeOptions);
 
       return client
-        .mutate(clientOptions)
+        .mutate(clientOptions as MutationOptions<TData, OperationVariables>)
         .then((response) => {
           const { data, errors } = response;
           const error =
@@ -102,7 +103,10 @@ export function useMutation<
             executeOptions.onError || ref.current.options?.onError;
 
           if (error && onError) {
-            onError(error, clientOptions);
+            onError(
+              error,
+              clientOptions as MutationOptions<TData, OperationVariables>
+            );
           }
 
           if (
@@ -126,7 +130,10 @@ export function useMutation<
             executeOptions.onCompleted || ref.current.options?.onCompleted;
 
           if (!error) {
-            onCompleted?.(response.data!, clientOptions);
+            onCompleted?.(
+              response.data!,
+              clientOptions as MutationOptions<TData, OperationVariables>
+            );
           }
 
           return response;
@@ -150,7 +157,10 @@ export function useMutation<
             executeOptions.onError || ref.current.options?.onError;
 
           if (onError) {
-            onError(error, clientOptions);
+            onError(
+              error,
+              clientOptions as MutationOptions<TData, OperationVariables>
+            );
 
             // TODO(brian): why are we returning this here???
             return { data: void 0, errors: error };
diff --git a/src/react/hooks/useQuery.ts b/src/react/hooks/useQuery.ts
--- a/src/react/hooks/useQuery.ts
+++ b/src/react/hooks/useQuery.ts
@@ -271,8 +271,8 @@ class InternalState<TData, TVariables extends OperationVariables> {
   // useQuery method, so we can safely use these members in other/later methods
   // without worrying they might be uninitialized.
   private renderPromises: ApolloContextValue["renderPromises"];
-  private queryHookOptions: QueryHookOptions<TData, TVariables>;
-  private watchQueryOptions: WatchQueryOptions<TVariables, TData>;
+  private queryHookOptions!: QueryHookOptions<TData, TVariables>;
+  private watchQueryOptions!: WatchQueryOptions<TVariables, TData>;
 
   private useOptions(options: QueryHookOptions<TData, TVariables>) {
     const watchQueryOptions = this.createWatchQueryOptions(
@@ -461,8 +461,8 @@ class InternalState<TData, TVariables extends OperationVariables> {
   private onCompleted(data: TData) {}
   private onError(error: ApolloError) {}
 
-  private observable: ObservableQuery<TData, TVariables>;
-  private obsQueryFields: Omit<
+  private observable!: ObservableQuery<TData, TVariables>;
+  private obsQueryFields!: Omit<
     ObservableQueryFields<TData, TVariables>,
     "variables"
   >;
diff --git a/src/react/hooks/useSuspenseQuery.ts b/src/react/hooks/useSuspenseQuery.ts
--- a/src/react/hooks/useSuspenseQuery.ts
+++ b/src/react/hooks/useSuspenseQuery.ts
@@ -178,7 +178,11 @@ export function useSuspenseQuery<
 ): UseSuspenseQueryResult<TData | undefined, TVariables> {
   const client = useApolloClient(options.client);
   const suspenseCache = getSuspenseCache(client);
-  const watchQueryOptions = useWatchQueryOptions({ client, query, options });
+  const watchQueryOptions = useWatchQueryOptions<any, any>({
+    client,
+    query,
+    options,
+  });
   const { fetchPolicy, variables } = watchQueryOptions;
   const { queryKey = [] } = options;
 
@@ -236,8 +240,8 @@ export function useSuspenseQuery<
 
   const result = fetchPolicy === "standby" ? skipResult : __use(promise);
 
-  const fetchMore: FetchMoreFunction<TData, TVariables> = React.useCallback(
-    (options) => {
+  const fetchMore = React.useCallback(
+    ((options) => {
       const promise = queryRef.fetchMore(options);
 
       setPromiseCache((previousPromiseCache) =>
@@ -245,7 +249,10 @@ export function useSuspenseQuery<
       );
 
       return promise;
-    },
+    }) satisfies FetchMoreFunction<
+      unknown,
+      OperationVariables
+    > as FetchMoreFunction<TData | undefined, TVariables>,
     [queryRef]
   );
 
@@ -262,13 +269,17 @@ export function useSuspenseQuery<
     [queryRef]
   );
 
-  const subscribeToMore: SubscribeToMoreFunction<TData, TVariables> =
-    React.useCallback(
-      (options) => queryRef.observable.subscribeToMore(options),
-      [queryRef]
-    );
+  const subscribeToMore: SubscribeToMoreFunction<
+    TData | undefined,
+    TVariables
+  > = React.useCallback(
+    (options) => queryRef.observable.subscribeToMore(options),
+    [queryRef]
+  );
 
-  return React.useMemo(() => {
+  return React.useMemo<
+    UseSuspenseQueryResult<TData | undefined, TVariables>
+  >(() => {
     return {
       client,
       data: result.data,
diff --git a/src/utilities/common/filterInPlace.ts b/src/utilities/common/filterInPlace.ts
deleted file mode 100644
--- a/src/utilities/common/filterInPlace.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-export function filterInPlace<T>(
-  array: T[],
-  test: (elem: T) => boolean,
-  context?: any
-): T[] {
-  let target = 0;
-  array.forEach(function (elem, i) {
-    if (test.call(this, elem, i, array)) {
-      array[target++] = elem;
-    }
-  }, context);
-  array.length = target;
-  return array;
-}
diff --git a/src/utilities/common/mergeDeep.ts b/src/utilities/common/mergeDeep.ts
--- a/src/utilities/common/mergeDeep.ts
+++ b/src/utilities/common/mergeDeep.ts
@@ -72,7 +72,7 @@ const defaultReconciler: ReconcilerFunction<any[]> = function (
 
 export class DeepMerger<TContextArgs extends any[]> {
   constructor(
-    private reconciler: ReconcilerFunction<TContextArgs> = defaultReconciler
+    private reconciler: ReconcilerFunction<TContextArgs> = defaultReconciler as any as ReconcilerFunction<TContextArgs>
   ) {}
 
   public merge(target: any, source: any, ...context: TContextArgs): any {
diff --git a/src/utilities/common/mergeOptions.ts b/src/utilities/common/mergeOptions.ts
--- a/src/utilities/common/mergeOptions.ts
+++ b/src/utilities/common/mergeOptions.ts
@@ -10,7 +10,7 @@ import { compact } from "./compact.js";
 type OptionsUnion<TData, TVariables extends OperationVariables, TContext> =
   | WatchQueryOptions<TVariables, TData>
   | QueryOptions<TVariables, TData>
-  | MutationOptions<TData, TVariables, TContext>;
+  | MutationOptions<TData, TVariables, TContext, any>;
 
 export function mergeOptions<
   TDefaultOptions extends Partial<OptionsUnion<any, any, any>>,
diff --git a/src/utilities/graphql/transform.ts b/src/utilities/graphql/transform.ts
--- a/src/utilities/graphql/transform.ts
+++ b/src/utilities/graphql/transform.ts
@@ -12,7 +12,7 @@ import type {
   FragmentSpreadNode,
   VariableDefinitionNode,
   ASTNode,
-  ASTVisitor,
+  ASTVisitFn,
   InlineFragmentNode,
 } from "graphql";
 import { visit, Kind } from "graphql";
@@ -29,6 +29,12 @@ import type { FragmentMap } from "./fragments.js";
 import { createFragmentMap } from "./fragments.js";
 import { isArray, isNonEmptyArray } from "../common/arrays.js";
 
+// https://github.com/graphql/graphql-js/blob/8d7c8fccf5a9846a50785de04abda58a7eb13fc0/src/language/visitor.ts#L20-L23
+interface EnterLeaveVisitor<TVisitedNode extends ASTNode> {
+  readonly enter?: ASTVisitFn<TVisitedNode>;
+  readonly leave?: ASTVisitFn<TVisitedNode>;
+}
+
 export type RemoveNodeConfig<N> = {
   name?: string;
   test?: (node: N) => boolean;
@@ -208,8 +214,10 @@ export function removeDirectivesFromDocument(
   // original doc immediately without any modifications.
   let firstVisitMadeChanges = false;
 
-  const fieldOrInlineFragmentVisitor: ASTVisitor = {
-    enter(node: FieldNode | InlineFragmentNode) {
+  const fieldOrInlineFragmentVisitor: EnterLeaveVisitor<
+    FieldNode | InlineFragmentNode
+  > = {
+    enter(node) {
       if (shouldRemoveField(node.directives)) {
         firstVisitMadeChanges = true;
         return null;
@@ -385,8 +393,10 @@ export function removeDirectivesFromDocument(
       )
     );
 
-  const enterVisitor: ASTVisitor = {
-    enter(node: FragmentSpreadNode | FragmentDefinitionNode) {
+  const enterVisitor: EnterLeaveVisitor<
+    FragmentSpreadNode | FragmentDefinitionNode
+  > = {
+    enter(node) {
       if (fragmentWillBeRemoved(node.name.value)) {
         return null;
       }
diff --git a/src/utilities/observables/Concast.ts b/src/utilities/observables/Concast.ts
--- a/src/utilities/observables/Concast.ts
+++ b/src/utilities/observables/Concast.ts
@@ -147,9 +147,9 @@ export class Concast<T> extends Observable<T> {
   // Any Concast object can be trivially converted to a Promise, without
   // having to create a new wrapper Observable. This promise provides an
   // easy way to observe the final state of the Concast.
-  private resolve: (result?: T | PromiseLike<T>) => void;
-  private reject: (reason: any) => void;
-  public readonly promise = new Promise<T>((resolve, reject) => {
+  private resolve!: (result?: T | PromiseLike<T>) => void;
+  private reject!: (reason: any) => void;
+  public readonly promise = new Promise<T | undefined>((resolve, reject) => {
     this.resolve = resolve;
     this.reject = reject;
   });
diff --git a/src/utilities/observables/Observable.ts b/src/utilities/observables/Observable.ts
--- a/src/utilities/observables/Observable.ts
+++ b/src/utilities/observables/Observable.ts
@@ -17,6 +17,7 @@ export type { Observer, ObservableSubscription, Subscriber };
 const { prototype } = Observable;
 const fakeObsSymbol = "@@observable" as keyof typeof prototype;
 if (!prototype[fakeObsSymbol]) {
+  // @ts-expect-error
   prototype[fakeObsSymbol] = function () {
     return this;
   };
diff --git a/src/utilities/policies/pagination.ts b/src/utilities/policies/pagination.ts
--- a/src/utilities/policies/pagination.ts
+++ b/src/utilities/policies/pagination.ts
@@ -42,7 +42,7 @@ export function offsetLimitPagination<T = Reference>(
           // to receive any arguments, so you might prefer to throw an
           // exception here, instead of recovering by appending incoming
           // onto the existing array.
-          merged.push.apply(merged, incoming);
+          merged.push(...incoming);
         }
       }
 
diff --git a/src/utilities/types/TODO.ts b/src/utilities/types/TODO.ts
new file mode 100644
--- /dev/null
+++ b/src/utilities/types/TODO.ts
@@ -0,0 +1,2 @@
+/** @internal */
+export type TODO = any;


Test patch:
diff --git a/src/__tests__/ApolloClient.ts b/src/__tests__/ApolloClient.ts
--- a/src/__tests__/ApolloClient.ts
+++ b/src/__tests__/ApolloClient.ts
@@ -4,7 +4,6 @@ import {
   ApolloClient,
   ApolloError,
   DefaultOptions,
-  FetchPolicy,
   QueryOptions,
   makeReference,
 } from "../core";
@@ -2126,8 +2125,8 @@ describe("ApolloClient", () => {
           }
         `;
 
-        ["network-only", "cache-and-network"].forEach(
-          (fetchPolicy: FetchPolicy) => {
+        (["network-only", "cache-and-network"] as const).forEach(
+          (fetchPolicy) => {
             const observable = client.watchQuery({
               query,
               fetchPolicy,
@@ -2156,13 +2155,15 @@ describe("ApolloClient", () => {
           }
         `;
 
-        [
-          "cache-first",
-          "cache-and-network",
-          "network-only",
-          "cache-only",
-          "no-cache",
-        ].forEach((fetchPolicy: FetchPolicy) => {
+        (
+          [
+            "cache-first",
+            "cache-and-network",
+            "network-only",
+            "cache-only",
+            "no-cache",
+          ] as const
+        ).forEach((fetchPolicy) => {
           const observable = client.watchQuery({
             query,
             fetchPolicy,
diff --git a/src/__tests__/client.ts b/src/__tests__/client.ts
--- a/src/__tests__/client.ts
+++ b/src/__tests__/client.ts
@@ -1821,7 +1821,7 @@ describe("client", () => {
         link,
 
         cache: new InMemoryCache({
-          dataIdFromObject: (obj: { id: any }) => obj.id,
+          dataIdFromObject: (obj: any) => obj.id,
           addTypename: false,
         }),
       });
@@ -1870,7 +1870,7 @@ describe("client", () => {
         callback();
         throw new Error("not reached");
       } catch (thrown) {
-        expect(thrown.message).toBe(cacheAndNetworkError);
+        expect((thrown as Error).message).toBe(cacheAndNetworkError);
       }
     }
 
diff --git a/src/__tests__/graphqlSubscriptions.ts b/src/__tests__/graphqlSubscriptions.ts
--- a/src/__tests__/graphqlSubscriptions.ts
+++ b/src/__tests__/graphqlSubscriptions.ts
@@ -488,7 +488,7 @@ describe("GraphQL Subscriptions", () => {
 
       client.subscribe(options).subscribe({
         next() {
-          expect(link.operation.getContext().someVar).toEqual(
+          expect(link.operation?.getContext().someVar).toEqual(
             options.context.someVar
           );
           resolve();
diff --git a/src/__tests__/local-state/general.ts b/src/__tests__/local-state/general.ts
--- a/src/__tests__/local-state/general.ts
+++ b/src/__tests__/local-state/general.ts
@@ -1207,7 +1207,7 @@ describe("Combining client and server state/operations", () => {
             watchCount += 1;
             client.mutate({
               mutation,
-              update(proxy, { data: { updateUser } }: { data: any }) {
+              update(proxy, { data: { updateUser } }) {
                 proxy.writeQuery({
                   query: userQuery,
                   data: {
diff --git a/src/__tests__/local-state/resolvers.ts b/src/__tests__/local-state/resolvers.ts
--- a/src/__tests__/local-state/resolvers.ts
+++ b/src/__tests__/local-state/resolvers.ts
@@ -11,7 +11,7 @@ import {
   WatchQueryOptions,
 } from "../../core";
 
-import { InMemoryCache } from "../../cache";
+import { InMemoryCache, isReference } from "../../cache";
 import { Observable, Observer } from "../../utilities";
 import { ApolloLink } from "../../link/core";
 import { itAsync } from "../../testing";
@@ -747,10 +747,13 @@ describe("Writing cache data from resolvers", () => {
                 },
               },
             });
-            cache.modify({
+            cache.modify<{ field: { field2: number } }>({
               id: "Object:uniqueId",
               fields: {
-                field(value: { field2: number }) {
+                field(value) {
+                  if (isReference(value)) {
+                    fail("Should not be a reference");
+                  }
                   expect(value.field2).toBe(1);
                   return { ...value, field2: 2 };
                 },
diff --git a/src/__tests__/optimistic.ts b/src/__tests__/optimistic.ts
--- a/src/__tests__/optimistic.ts
+++ b/src/__tests__/optimistic.ts
@@ -235,7 +235,7 @@ describe("optimistic mutation results", () => {
             await promise;
           } catch (err) {
             expect(err).toBeInstanceOf(Error);
-            expect(err.message).toBe("forbidden (test error)");
+            expect((err as Error).message).toBe("forbidden (test error)");
 
             const dataInStore = (client.cache as InMemoryCache).extract(true);
             expect((dataInStore["TodoList5"] as any).todos.length).toBe(3);
@@ -489,7 +489,7 @@ describe("optimistic mutation results", () => {
             await promise;
           } catch (err) {
             expect(err).toBeInstanceOf(Error);
-            expect(err.message).toBe("forbidden (test error)");
+            expect((err as Error).message).toBe("forbidden (test error)");
 
             const dataInStore = (client.cache as InMemoryCache).extract(true);
             expect((dataInStore["TodoList5"] as any).todos.length).toBe(3);
@@ -2019,11 +2019,12 @@ describe("optimistic mutation results", () => {
       const wrapReject = <TArgs extends any[], TResult>(
         fn: (...args: TArgs) => TResult
       ): typeof fn => {
-        return function () {
+        return function (this: unknown, ...args: TArgs) {
           try {
-            return fn.apply(this, arguments);
+            return fn.apply(this, args);
           } catch (e) {
             reject(e);
+            throw e;
           }
         };
       };
diff --git a/src/__tests__/subscribeToMore.ts b/src/__tests__/subscribeToMore.ts
--- a/src/__tests__/subscribeToMore.ts
+++ b/src/__tests__/subscribeToMore.ts
@@ -8,8 +8,10 @@ import { itAsync, mockSingleLink, mockObservableLink } from "../testing";
 
 const isSub = (operation: Operation) =>
   (operation.query as DocumentNode).definitions
-    .filter((x) => x.kind === "OperationDefinition")
-    .some((x: OperationDefinitionNode) => x.operation === "subscription");
+    .filter(
+      (x): x is OperationDefinitionNode => x.kind === "OperationDefinition"
+    )
+    .some((x) => x.operation === "subscription");
 
 describe("subscribeToMore", () => {
   const query = gql`
diff --git a/src/cache/inmemory/__tests__/cache.ts b/src/cache/inmemory/__tests__/cache.ts
--- a/src/cache/inmemory/__tests__/cache.ts
+++ b/src/cache/inmemory/__tests__/cache.ts
@@ -4035,6 +4035,7 @@ describe("ReactiveVar and makeVar", () => {
     let broadcastCount = 0;
     cache["broadcastWatches"] = function () {
       ++broadcastCount;
+      // @ts-expect-error
       return broadcast.apply(this, arguments);
     };
 
diff --git a/src/cache/inmemory/__tests__/diffAgainstStore.ts b/src/cache/inmemory/__tests__/diffAgainstStore.ts
--- a/src/cache/inmemory/__tests__/diffAgainstStore.ts
+++ b/src/cache/inmemory/__tests__/diffAgainstStore.ts
@@ -493,8 +493,8 @@ describe("diffing queries against the store", () => {
     };
 
     const cache = new InMemoryCache({
-      dataIdFromObject({ id }: { id: string }) {
-        return id;
+      dataIdFromObject(obj: any) {
+        return obj.id;
       },
     });
 
@@ -841,7 +841,7 @@ describe("diffing queries against the store", () => {
 
       const writer = new StoreWriter(
         new InMemoryCache({
-          dataIdFromObject: ({ id }: { id: string }) => id,
+          dataIdFromObject: (obj: any) => obj.id,
         })
       );
 
@@ -1067,7 +1067,7 @@ describe("diffing queries against the store", () => {
         });
         throw new Error("should have thrown");
       } catch (e) {
-        expect(e.message).toEqual(
+        expect((e as Error).message).toEqual(
           "Missing selection set for object of type Message returned for query field messageList"
         );
       }
diff --git a/src/cache/inmemory/__tests__/readFromStore.ts b/src/cache/inmemory/__tests__/readFromStore.ts
--- a/src/cache/inmemory/__tests__/readFromStore.ts
+++ b/src/cache/inmemory/__tests__/readFromStore.ts
@@ -1904,7 +1904,7 @@ describe("reading from the store", () => {
           );
           expect(value.__ref).toBe('Deity:{"name":"Zeus"}');
           // Interim ruler Apollo takes over for real.
-          return toReference(apolloRulerResult.ruler);
+          return toReference(apolloRulerResult.ruler)!;
         },
       },
     });
diff --git a/src/cache/inmemory/__tests__/roundtrip.ts b/src/cache/inmemory/__tests__/roundtrip.ts
--- a/src/cache/inmemory/__tests__/roundtrip.ts
+++ b/src/cache/inmemory/__tests__/roundtrip.ts
@@ -61,7 +61,7 @@ function storeRoundtrip(query: DocumentNode, result: any, variables = {}) {
       (immutableResult as any).illegal = "this should not work";
       throw new Error("unreached");
     } catch (e) {
-      expect(e.message).not.toMatch(/unreached/);
+      expect((e as Error).message).not.toMatch(/unreached/);
       expect(e).toBeInstanceOf(TypeError);
     }
     assertDeeplyFrozen(immutableResult);
diff --git a/src/cache/inmemory/__tests__/writeToStore.ts b/src/cache/inmemory/__tests__/writeToStore.ts
--- a/src/cache/inmemory/__tests__/writeToStore.ts
+++ b/src/cache/inmemory/__tests__/writeToStore.ts
@@ -25,9 +25,14 @@ import { defaultNormalizedCacheFactory, writeQueryToStore } from "./helpers";
 import { InMemoryCache } from "../inMemoryCache";
 import { TypedDocumentNode } from "../../../core";
 import { extractFragmentContext } from "../helpers";
+import { KeyFieldsFunction } from "../policies";
+import { invariant } from "../../../utilities/globals";
 import { spyOnConsole } from "../../../testing/internal";
 
-const getIdField = ({ id }: { id: string }) => id;
+const getIdField: KeyFieldsFunction = ({ id }) => {
+  invariant(typeof id === "string", "id is not a string");
+  return id;
+};
 
 describe("writing to the store", () => {
   const cache = new InMemoryCache({
@@ -1293,19 +1298,17 @@ describe("writing to the store", () => {
     }
 
     testData.forEach((data) => {
-      data.mutation.definitions.forEach(
-        (definition: OperationDefinitionNode) => {
-          if (isOperationDefinition(definition)) {
-            definition.selectionSet.selections.forEach((selection) => {
-              if (isField(selection)) {
-                expect(
-                  storeKeyNameFromField(selection, data.variables)
-                ).toEqual(data.expected);
-              }
-            });
-          }
+      data.mutation.definitions.forEach((definition) => {
+        if (isOperationDefinition(definition)) {
+          definition.selectionSet.selections.forEach((selection) => {
+            if (isField(selection)) {
+              expect(storeKeyNameFromField(selection, data.variables)).toEqual(
+                data.expected
+              );
+            }
+          });
         }
-      );
+      });
     });
   });
 
@@ -1357,7 +1360,7 @@ describe("writing to the store", () => {
       return value.kind === "OperationDefinition";
     }
 
-    mutation.definitions.map((def: OperationDefinitionNode) => {
+    mutation.definitions.map((def) => {
       if (isOperationDefinition(def)) {
         const writer = new StoreWriter(
           new InMemoryCache({
diff --git a/src/core/__tests__/ObservableQuery.ts b/src/core/__tests__/ObservableQuery.ts
--- a/src/core/__tests__/ObservableQuery.ts
+++ b/src/core/__tests__/ObservableQuery.ts
@@ -46,7 +46,8 @@ export const mockFetchQuery = (queryManager: QueryManager<any>) => {
   >(
     original: T
   ) =>
-    jest.fn<ReturnType<T>, Parameters<T>>(function () {
+    jest.fn<ReturnType<T>, Parameters<T>>(function (): ReturnType<T> {
+      // @ts-expect-error
       return original.apply(queryManager, arguments);
     });
 
@@ -2740,7 +2741,7 @@ describe("ObservableQuery", () => {
             throw new Error("not reached");
           } catch (error) {
             expect(error).toBeInstanceOf(TypeError);
-            expect(error.message).toMatch(
+            expect((error as Error).message).toMatch(
               /Cannot assign to read only property 'value'/
             );
           }
diff --git a/src/core/__tests__/QueryManager/index.ts b/src/core/__tests__/QueryManager/index.ts
--- a/src/core/__tests__/QueryManager/index.ts
+++ b/src/core/__tests__/QueryManager/index.ts
@@ -1610,7 +1610,7 @@ describe("QueryManager", () => {
     });
   });
 
-  const getIdField = ({ id }: { id: string }) => id;
+  const getIdField = (obj: any) => obj.id;
 
   itAsync(
     "runs a mutation with object parameters and puts the result in the store",
diff --git a/src/link/batch-http/__tests__/batchHttpLink.ts b/src/link/batch-http/__tests__/batchHttpLink.ts
--- a/src/link/batch-http/__tests__/batchHttpLink.ts
+++ b/src/link/batch-http/__tests__/batchHttpLink.ts
@@ -35,10 +35,11 @@ function makeCallback<TArgs extends any[]>(
 ) {
   return function () {
     try {
+      // @ts-expect-error
       callback.apply(this, arguments);
       resolve();
     } catch (error) {
-      reject(error);
+      reject(error as Error);
     }
   } as typeof callback;
 }
@@ -472,7 +473,7 @@ describe("SharedHttpTest", () => {
 
           after();
         } catch (e) {
-          reject(e);
+          reject(e as Error);
         }
       },
     });
diff --git a/src/link/batch/__tests__/batchLink.ts b/src/link/batch/__tests__/batchLink.ts
--- a/src/link/batch/__tests__/batchLink.ts
+++ b/src/link/batch/__tests__/batchLink.ts
@@ -64,6 +64,7 @@ function terminatingCheck<TArgs extends any[]>(
 ) {
   return function () {
     try {
+      // @ts-expect-error
       callback.apply(this, arguments);
       resolve();
     } catch (error) {
diff --git a/src/link/http/__tests__/HttpLink.ts b/src/link/http/__tests__/HttpLink.ts
--- a/src/link/http/__tests__/HttpLink.ts
+++ b/src/link/http/__tests__/HttpLink.ts
@@ -92,10 +92,11 @@ function makeCallback<TArgs extends any[]>(
 ) {
   return function () {
     try {
+      // @ts-expect-error
       callback.apply(this, arguments);
       resolve();
     } catch (error) {
-      reject(error);
+      reject(error as Error);
     }
   } as typeof callback;
 }
@@ -1202,7 +1203,7 @@ describe("HttpLink", () => {
         reject("warning wasn't called");
       } catch (e) {
         makeCallback(resolve, reject, () =>
-          expect(e.message).toMatch(/has not been found globally/)
+          expect((e as Error).message).toMatch(/has not been found globally/)
         )();
       }
     });
@@ -1214,7 +1215,7 @@ describe("HttpLink", () => {
         reject("warning wasn't called");
       } catch (e) {
         makeCallback(resolve, reject, () =>
-          expect(e.message).toMatch(/has not been found globally/)
+          expect((e as Error).message).toMatch(/has not been found globally/)
         )();
       }
     });
diff --git a/src/link/http/__tests__/responseIterator.ts b/src/link/http/__tests__/responseIterator.ts
--- a/src/link/http/__tests__/responseIterator.ts
+++ b/src/link/http/__tests__/responseIterator.ts
@@ -17,10 +17,11 @@ function makeCallback<TArgs extends any[]>(
 ) {
   return function () {
     try {
+      // @ts-expect-error
       callback.apply(this, arguments);
       resolve();
     } catch (error) {
-      reject(error);
+      reject(error as Error);
     }
   } as typeof callback;
 }
diff --git a/src/link/persisted-queries/__tests__/persisted-queries.test.ts b/src/link/persisted-queries/__tests__/persisted-queries.test.ts
--- a/src/link/persisted-queries/__tests__/persisted-queries.test.ts
+++ b/src/link/persisted-queries/__tests__/persisted-queries.test.ts
@@ -219,7 +219,7 @@ describe("happy path", () => {
         reject("should have thrown an error");
       } catch (error) {
         expect(
-          error.message.indexOf(
+          (error as Error).message.indexOf(
             'Missing/invalid "sha256" or "generateHash" function'
           )
         ).toBe(0);
@@ -238,7 +238,7 @@ describe("happy path", () => {
           reject("should have thrown an error");
         } catch (error) {
           expect(
-            error.message.indexOf(
+            (error as Error).message.indexOf(
               'Missing/invalid "sha256" or "generateHash" function'
             )
           ).toBe(0);
@@ -569,6 +569,7 @@ describe("failure path", () => {
               status,
             });
           }
+          // @ts-expect-error
           return global.fetch.apply(null, args);
         };
         const link = createPersistedQuery({ sha256 }).concat(
@@ -623,6 +624,7 @@ describe("failure path", () => {
             status,
           });
         }
+        // @ts-expect-error
         return global.fetch.apply(null, args);
       };
       const link = createPersistedQuery({ sha256 }).concat(
@@ -662,6 +664,7 @@ describe("failure path", () => {
               status,
             });
           }
+          // @ts-expect-error
           return global.fetch.apply(null, args);
         };
 
diff --git a/src/react/hoc/__tests__/fragments.test.tsx b/src/react/hoc/__tests__/fragments.test.tsx
--- a/src/react/hoc/__tests__/fragments.test.tsx
+++ b/src/react/hoc/__tests__/fragments.test.tsx
@@ -55,7 +55,7 @@ describe("fragments", () => {
       );
       throw new Error();
     } catch (e) {
-      expect(e.name).toMatch(/Invariant Violation/);
+      expect((e as Error).name).toMatch(/Invariant Violation/);
     }
   });
 
diff --git a/src/react/hoc/__tests__/queries/errors.test.tsx b/src/react/hoc/__tests__/queries/errors.test.tsx
--- a/src/react/hoc/__tests__/queries/errors.test.tsx
+++ b/src/react/hoc/__tests__/queries/errors.test.tsx
@@ -105,7 +105,7 @@ describe("[queries] errors", () => {
 
     try {
       unmount();
-    } catch (e) {
+    } catch (e: any) {
       throw new Error(e);
     }
   });
@@ -220,6 +220,7 @@ describe("[queries] errors", () => {
         "setVar",
         1
       )(
+        // @ts-expect-error
         graphql<Props, Data, Vars>(query)(
           class extends React.Component<ChildProps<Props, Data, Vars>> {
             componentDidUpdate() {
diff --git a/src/react/hoc/__tests__/queries/updateQuery.test.tsx b/src/react/hoc/__tests__/queries/updateQuery.test.tsx
--- a/src/react/hoc/__tests__/queries/updateQuery.test.tsx
+++ b/src/react/hoc/__tests__/queries/updateQuery.test.tsx
@@ -143,7 +143,7 @@ describe("[queries] updateQuery", () => {
             ).toBeTruthy();
             try {
               this.props.data!.updateQuery((p) => p);
-            } catch (e) {
+            } catch (e: any) {
               // TODO: branch never hit in test
               expect(e.toString()).toMatch(
                 /ObservableQuery with this id doesn't exist:/
diff --git a/src/react/hoc/__tests__/ssr/getDataFromTree.test.tsx b/src/react/hoc/__tests__/ssr/getDataFromTree.test.tsx
--- a/src/react/hoc/__tests__/ssr/getDataFromTree.test.tsx
+++ b/src/react/hoc/__tests__/ssr/getDataFromTree.test.tsx
@@ -945,6 +945,7 @@ describe("SSR", () => {
         </div>
       );
 
+      // @ts-expect-error
       const WrappedElement = withQuery(withMutation(Element));
 
       const app = (
diff --git a/src/testing/core/itAsync.ts b/src/testing/core/itAsync.ts
--- a/src/testing/core/itAsync.ts
+++ b/src/testing/core/itAsync.ts
@@ -9,7 +9,7 @@ function wrap(key?: "only" | "skip" | "todo") {
   ) =>
     (key ? it[key] : it)(
       message,
-      function () {
+      function (this: unknown) {
         return new Promise((resolve, reject) =>
           callback.call(this, resolve, reject)
         );
@@ -21,7 +21,7 @@ function wrap(key?: "only" | "skip" | "todo") {
 const wrappedIt = wrap();
 
 export const itAsync = Object.assign(
-  function (...args: Parameters<typeof wrappedIt>) {
+  function (this: unknown, ...args: Parameters<typeof wrappedIt>) {
     return wrappedIt.apply(this, args);
   },
   {
diff --git a/src/testing/core/mocking/mockLink.ts b/src/testing/core/mocking/mockLink.ts
--- a/src/testing/core/mocking/mockLink.ts
+++ b/src/testing/core/mocking/mockLink.ts
@@ -45,7 +45,7 @@ function requestToKey(request: GraphQLRequest, addTypename: Boolean): string {
 }
 
 export class MockLink extends ApolloLink {
-  public operation: Operation;
+  public operation!: Operation;
   public addTypename: Boolean = true;
   public showWarnings: boolean = true;
   private mockedResponsesByKey: { [key: string]: MockedResponse[] } = {};
diff --git a/src/testing/core/mocking/mockSubscriptionLink.ts b/src/testing/core/mocking/mockSubscriptionLink.ts
--- a/src/testing/core/mocking/mockSubscriptionLink.ts
+++ b/src/testing/core/mocking/mockSubscriptionLink.ts
@@ -15,7 +15,7 @@ export interface MockedSubscriptionResult {
 export class MockSubscriptionLink extends ApolloLink {
   public unsubscribers: any[] = [];
   public setups: any[] = [];
-  public operation: Operation;
+  public operation?: Operation;
 
   private observers: any[] = [];
 
diff --git a/src/testing/core/observableToPromise.ts b/src/testing/core/observableToPromise.ts
--- a/src/testing/core/observableToPromise.ts
+++ b/src/testing/core/observableToPromise.ts
@@ -11,7 +11,7 @@ import type { ObservableSubscription } from "../../utilities/index.js";
  * @param errorCallbacks an expected set of errors
  */
 export type Options = {
-  observable: ObservableQuery<any>;
+  observable: ObservableQuery<any, any>;
   shouldResolve?: boolean;
   wait?: number;
   errorCallbacks?: ((error: Error) => any)[];
diff --git a/src/testing/core/withConsoleSpy.ts b/src/testing/core/withConsoleSpy.ts
--- a/src/testing/core/withConsoleSpy.ts
+++ b/src/testing/core/withConsoleSpy.ts
@@ -2,8 +2,7 @@ function wrapTestFunction(
   fn: (...args: any[]) => any,
   consoleMethodName: "log" | "warn" | "error"
 ) {
-  return function () {
-    const args = arguments;
+  return function (this: any, ...args: any[]) {
     const spy = jest.spyOn(console, consoleMethodName);
     spy.mockImplementation(() => {});
     return new Promise((resolve) => {
diff --git a/src/testing/internal/profile/profile.tsx b/src/testing/internal/profile/profile.tsx
--- a/src/testing/internal/profile/profile.tsx
+++ b/src/testing/internal/profile/profile.tsx
@@ -203,7 +203,7 @@ export function profile<
         return render;
       },
       async takeRender(options: NextRenderOptions = {}) {
-        let error: { message?: string } | undefined = undefined;
+        let error: unknown = undefined;
         try {
           return await Profiled.peekRender({
             [_stackTrace]: captureStackTrace(Profiled.takeRender),
diff --git a/src/testing/matchers/ProfiledComponent.ts b/src/testing/matchers/ProfiledComponent.ts
--- a/src/testing/matchers/ProfiledComponent.ts
+++ b/src/testing/matchers/ProfiledComponent.ts
@@ -6,10 +6,10 @@ import type {
   ProfiledHook,
 } from "../internal/index.js";
 export const toRerender: MatcherFunction<[options?: NextRenderOptions]> =
-  async function (
-    _profiled: ProfiledComponent<any, any> | ProfiledHook<any, any>,
-    options?: NextRenderOptions
-  ) {
+  async function (actual, options) {
+    const _profiled = actual as
+      | ProfiledComponent<any, any>
+      | ProfiledHook<any, any>;
     const profiled =
       "ProfiledComponent" in _profiled
         ? _profiled.ProfiledComponent
@@ -42,11 +42,10 @@ const failed = {};
 
 export const toRenderExactlyTimes: MatcherFunction<
   [times: number, options?: NextRenderOptions]
-> = async function (
-  _profiled: ProfiledComponent<any, any> | ProfiledHook<any, any>,
-  times: number,
-  optionsPerRender?: NextRenderOptions
-) {
+> = async function (actual, times, optionsPerRender) {
+  const _profiled = actual as
+    | ProfiledComponent<any, any>
+    | ProfiledHook<any, any>;
   const profiled =
     "ProfiledComponent" in _profiled ? _profiled.ProfiledComponent : _profiled;
   const options = { timeout: 100, ...optionsPerRender };
diff --git a/src/utilities/common/__tests__/mergeDeep.ts b/src/utilities/common/__tests__/mergeDeep.ts
--- a/src/utilities/common/__tests__/mergeDeep.ts
+++ b/src/utilities/common/__tests__/mergeDeep.ts
@@ -146,7 +146,7 @@ describe("mergeDeep", function () {
   });
 
   it("supports custom reconciler functions", function () {
-    const merger = new DeepMerger((target, source, key) => {
+    const merger = new DeepMerger(function (target, source, key) {
       const targetValue = target[key];
       const sourceValue = source[key];
       if (Array.isArray(sourceValue)) {
diff --git a/src/utilities/observables/__tests__/Observable.ts b/src/utilities/observables/__tests__/Observable.ts
--- a/src/utilities/observables/__tests__/Observable.ts
+++ b/src/utilities/observables/__tests__/Observable.ts
@@ -39,8 +39,13 @@ describe("Observable", () => {
       return constructor as any;
     }
 
+    type ObservableWithSub<T> = Observable<T> & { sub?: Subscriber<T> };
+
     it("simulating super(sub) with Observable.call(this, sub)", () => {
-      function SubclassWithSuperCall<T>(sub: Subscriber<T>) {
+      function SubclassWithSuperCall<T>(
+        this: ObservableWithSub<T>,
+        sub: Subscriber<T>
+      ) {
         const self = Observable.call(this, sub) || this;
         self.sub = sub;
         return self;
@@ -49,7 +54,10 @@ describe("Observable", () => {
     });
 
     it("simulating super(sub) with Observable.apply(this, arguments)", () => {
-      function SubclassWithSuperApplyArgs<T>(_sub: Subscriber<T>) {
+      function SubclassWithSuperApplyArgs<T>(
+        this: ObservableWithSub<T>,
+        _sub: Subscriber<T>
+      ) {
         const self = Observable.apply(this, arguments) || this;
         self.sub = _sub;
         return self;
@@ -58,7 +66,10 @@ describe("Observable", () => {
     });
 
     it("simulating super(sub) with Observable.apply(this, [sub])", () => {
-      function SubclassWithSuperApplyArray<T>(...args: [Subscriber<T>]) {
+      function SubclassWithSuperApplyArray<T>(
+        this: ObservableWithSub<T>,
+        ...args: [Subscriber<T>]
+      ) {
         const self = Observable.apply(this, args) || this;
         self.sub = args[0];
         return self;
diff --git a/src/utilities/observables/__tests__/asyncMap.ts b/src/utilities/observables/__tests__/asyncMap.ts
--- a/src/utilities/observables/__tests__/asyncMap.ts
+++ b/src/utilities/observables/__tests__/asyncMap.ts
@@ -25,6 +25,7 @@ function rejectExceptions<Args extends any[], Ret>(
 ) {
   return function () {
     try {
+      // @ts-expect-error
       return fn.apply(this, arguments);
     } catch (error) {
       reject(error);
diff --git a/tsconfig.tests.json b/tsconfig.tests.json
--- a/tsconfig.tests.json
+++ b/tsconfig.tests.json
@@ -1,5 +1,3 @@
 {
-  "extends": "./tsconfig.json",
-  "include": ["src/**/__tests__/**/*.ts", "src/**/__tests__/**/*.tsx"],
-  "exclude": []
+  "extends": "./src/tsconfig.json"
 }