diff --git a/.eslintrc.js b/.eslintrc.js
index 3c1c75d30fbc93b8cbb397478c3b40ab61a0f37b..8fb34b3e6e17e2087f2f63fc25f406b7d86863a3 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -42,6 +42,7 @@ module.exports = {
     'prefer-destructuring': 0,
     'prefer-template': 0,
     'no-underscore-dangle': 0,
+    // 'no-unused-vars': 2,
 
     'sort-imports': [
       'error',
@@ -104,8 +105,9 @@ module.exports = {
     '@typescript-eslint/ban-types': 1,
   },
   settings: {
-    // https://github.com/benmosher/eslint-plugin-import/issues/1618
-    'import/internal-regex': '^type\\-fest$',
+    'import/parsers': {
+      '@typescript-eslint/parser': ['.ts'],
+    },
   },
   overrides: [
     {
@@ -130,6 +132,8 @@ module.exports = {
 
       rules: {
         '@typescript-eslint/explicit-function-return-type': 0,
+        '@typescript-eslint/explicit-module-boundary-types': 0,
+        '@typescript-eslint/restrict-template-expressions': 0,
       },
     },
   ],
diff --git a/lib/manager/gradle/__testutil__/gradle.ts b/lib/manager/gradle/__testutil__/gradle.ts
index 63e603c27fc2209616c343e4a1a481b81580900f..444accb9a872d1466f6e4b3644d10e7aee56698b 100644
--- a/lib/manager/gradle/__testutil__/gradle.ts
+++ b/lib/manager/gradle/__testutil__/gradle.ts
@@ -49,7 +49,7 @@ function determineJavaVersion(): number {
       throw Error(
         `This test suite needs Java and ${failIfNoJavaEnv} is set.
 Result of java -version:
-${error}`
+${error.toString()}`
       );
     }
     cachedJavaVersion = parseJavaVersion(javaVersionCommand.stderr);
@@ -60,13 +60,16 @@ ${error}`
 class WithGradle {
   private gradleSupportsThisJavaVersion: boolean;
 
-  constructor(private gradleVersion: number) {
+  constructor(gradleVersion: number) {
     const javaVersion = determineJavaVersion();
     if (gradleJavaVersionSupport[gradleVersion] === undefined) {
       throw Error(`Unknown gradle version '${gradleVersion}'!`);
     }
 
-    const supportedJavaVersions = gradleJavaVersionSupport[gradleVersion];
+    const supportedJavaVersions = gradleJavaVersionSupport[gradleVersion] as {
+      min: number;
+      max: number;
+    };
     this.gradleSupportsThisJavaVersion =
       javaVersion >= supportedJavaVersions.min &&
       javaVersion <= supportedJavaVersions.max;
diff --git a/lib/manager/npm/extract/common.ts b/lib/manager/npm/extract/common.ts
index b271b010a7695688884f5b278220c13a19805e83..da1f53429549cb72d95ececfdf7ef45962bf42c9 100644
--- a/lib/manager/npm/extract/common.ts
+++ b/lib/manager/npm/extract/common.ts
@@ -1,5 +1,4 @@
-// eslint-disable-next-line import/no-unresolved
-import { PackageJson } from 'type-fest';
+import type { PackageJson } from 'type-fest';
 
 export type NpmPackageDependeny = PackageJson.Dependency;
 
diff --git a/lib/platform/azure/azure-helper.ts b/lib/platform/azure/azure-helper.ts
index 7f9f7dea8efcb469d0ecaf2e7d577e51f76a3429..44b11e3e0c3307ffa776b8691cc1d72560bcdd7e 100644
--- a/lib/platform/azure/azure-helper.ts
+++ b/lib/platform/azure/azure-helper.ts
@@ -4,10 +4,10 @@ import {
   GitPullRequestMergeStrategy,
   GitRef,
 } from 'azure-devops-node-api/interfaces/GitInterfaces';
-import { Options } from 'simple-git';
 import { logger } from '../../logger';
 
 import { HostRule, PrState } from '../../types';
+import { GitOptions } from '../../types/git';
 import * as azureApi from './azure-got-wrapper';
 import { AzurePr } from './types';
 
@@ -17,7 +17,7 @@ function toBase64(from: string): string {
   return Buffer.from(from).toString('base64');
 }
 
-export function getStorageExtraCloneOpts(config: HostRule): Options {
+export function getStorageExtraCloneOpts(config: HostRule): GitOptions {
   let header: string;
   const headerName = 'AUTHORIZATION';
   if (!config.token && config.username && config.password) {
diff --git a/lib/platform/azure/index.ts b/lib/platform/azure/index.ts
index db369c992b4b5b3100dec6165fee813b77ec25d7..48c7312bdf1c614d85bb00863bf64ca9fbfed112 100644
--- a/lib/platform/azure/index.ts
+++ b/lib/platform/azure/index.ts
@@ -53,7 +53,10 @@ interface User {
 
 let config: Config = {} as any;
 
-const defaults: any = {
+const defaults: {
+  endpoint?: string;
+  hostType: string;
+} = {
   hostType: PLATFORM_TYPE_AZURE,
 };
 
@@ -252,8 +255,8 @@ export async function findPr({
         prsFiltered = prsFiltered.filter((item) => item.state === state);
         break;
     }
-  } catch (error) {
-    logger.error('findPr ' + error);
+  } catch (err) {
+    logger.error({ err }, 'findPr error');
   }
   if (prsFiltered.length === 0) {
     return null;
@@ -595,7 +598,7 @@ export async function addAssignees(
   issueNo: number,
   assignees: string[]
 ): Promise<void> {
-  logger.trace(`addAssignees(${issueNo}, ${assignees})`);
+  logger.trace(`addAssignees(${issueNo}, [${assignees.join(', ')}])`);
   const ids = await getUserIds(assignees);
   await ensureComment({
     number: issueNo,
@@ -613,7 +616,7 @@ export async function addReviewers(
   prNo: number,
   reviewers: string[]
 ): Promise<void> {
-  logger.trace(`addReviewers(${prNo}, ${reviewers})`);
+  logger.trace(`addReviewers(${prNo}, [${reviewers.join(', ')}])`);
   const azureApiGit = await azureApi.gitApi();
 
   const ids = await getUserIds(reviewers);
diff --git a/lib/platform/bitbucket-server/index.ts b/lib/platform/bitbucket-server/index.ts
index a4336e79cb3051f48b8c98a44f1fe6a11b22d1af..12b8b999c3e85d362774e1f8a254fdb6dc7ac898 100644
--- a/lib/platform/bitbucket-server/index.ts
+++ b/lib/platform/bitbucket-server/index.ts
@@ -1,6 +1,7 @@
 import url, { URLSearchParams } from 'url';
 import is from '@sindresorhus/is';
 import delay from 'delay';
+import type { PartialDeep } from 'type-fest';
 import {
   REPOSITORY_CHANGED,
   REPOSITORY_DISABLED,
@@ -10,6 +11,7 @@ import {
 import { PLATFORM_TYPE_BITBUCKET_SERVER } from '../../constants/platforms';
 import { logger } from '../../logger';
 import { BranchStatus, PrState } from '../../types';
+import { GitProtocol } from '../../types/git';
 import * as git from '../../util/git';
 import { deleteBranch } from '../../util/git';
 import * as hostRules from '../../util/host-rules';
@@ -40,7 +42,7 @@ import {
 import { smartTruncate } from '../utils/pr-body';
 import { BbbsRestPr, BbsConfig, BbsPr, BbsRestUserRef } from './types';
 import * as utils from './utils';
-import { PartialDeep } from 'type-fest';
+
 /*
  * Version: 5.3 (EOL Date: 15 Aug 2019)
  * See following docs for api information:
@@ -55,7 +57,10 @@ let config: BbsConfig = {} as any;
 
 const bitbucketServerHttp = new BitbucketServerHttp();
 
-const defaults: any = {
+const defaults: {
+  endpoint?: string;
+  hostType: string;
+} = {
   hostType: PLATFORM_TYPE_BITBUCKET_SERVER,
 };
 
@@ -143,7 +148,7 @@ export async function initRepo({
         `./rest/api/1.0/projects/${projectKey}/repos/${repositorySlug}/browse/renovate.json?limit=20000`
       );
       if (!body.isLastPage) {
-        logger.warn('Renovate config to big: ' + body.size);
+        logger.warn({ size: body.size }, `Renovate config to big`);
       } else {
         renovateConfig = JSON.parse(body.lines.join());
       }
@@ -169,9 +174,9 @@ export async function initRepo({
     config.bbUseDefaultReviewers = true;
   }
 
-  const { host, pathname } = url.parse(defaults.endpoint!);
+  const { host, pathname } = url.parse(defaults.endpoint);
   const gitUrl = git.getUrl({
-    protocol: defaults.endpoint!.split(':')[0],
+    protocol: defaults.endpoint.split(':')[0] as GitProtocol,
     auth: `${opts.username}:${opts.password}`,
     host: `${host}${pathname}${
       pathname.endsWith('/') ? '' : /* istanbul ignore next */ '/'
@@ -562,7 +567,7 @@ export /* istanbul ignore next */ function ensureIssueClosing(
 }
 
 export function addAssignees(iid: number, assignees: string[]): Promise<void> {
-  logger.debug(`addAssignees(${iid}, ${assignees})`);
+  logger.debug(`addAssignees(${iid}, [${assignees.join(', ')}])`);
   // TODO: Needs implementation
   // Currently Renovate does "Create PR" and then "Add assignee" as a two-step process, with this being the second step.
   // BB Server doesnt support assignees
@@ -573,7 +578,7 @@ export async function addReviewers(
   prNo: number,
   reviewers: string[]
 ): Promise<void> {
-  logger.debug(`Adding reviewers ${reviewers} to #${prNo}`);
+  logger.debug(`Adding reviewers '${reviewers.join(', ')}' to #${prNo}`);
 
   try {
     const pr = await getPr(prNo);
@@ -602,7 +607,10 @@ export async function addReviewers(
     } else if (err.statusCode === 409) {
       throw new Error(REPOSITORY_CHANGED);
     } else {
-      logger.fatal({ err }, `Failed to add reviewers ${reviewers} to #${prNo}`);
+      logger.fatal(
+        { err },
+        `Failed to add reviewers '${reviewers.join(', ')}' to #${prNo}`
+      );
       throw err;
     }
   }
diff --git a/lib/platform/bitbucket/index.ts b/lib/platform/bitbucket/index.ts
index 57c02fe2c0c2a813657cb52c6df46bff712d9680..0dcd312d5c022bb91822fbe1bfaee81c7338c5b0 100644
--- a/lib/platform/bitbucket/index.ts
+++ b/lib/platform/bitbucket/index.ts
@@ -40,7 +40,7 @@ const BITBUCKET_PROD_ENDPOINT = 'https://api.bitbucket.org/';
 
 let config: utils.Config = {} as any;
 
-let endpoint_ = BITBUCKET_PROD_ENDPOINT;
+const defaults = { endpoint: BITBUCKET_PROD_ENDPOINT };
 
 export function initPlatform({
   endpoint,
@@ -56,9 +56,9 @@ export function initPlatform({
     logger.warn(
       `Init: Bitbucket Cloud endpoint should generally be ${BITBUCKET_PROD_ENDPOINT} but is being configured to a different value. Did you mean to use Bitbucket Server?`
     );
-    endpoint_ = endpoint;
+    defaults.endpoint = endpoint;
   }
-  setBaseUrl(endpoint_);
+  setBaseUrl(defaults.endpoint);
   // TODO: Add a connection check that endpoint/username/password combination are valid
   const platformConfig: PlatformResult = {
     endpoint: endpoint || BITBUCKET_PROD_ENDPOINT,
@@ -90,7 +90,7 @@ export async function initRepo({
   logger.debug(`initRepo("${repository}")`);
   const opts = hostRules.find({
     hostType: PLATFORM_TYPE_BITBUCKET,
-    url: endpoint_,
+    url: defaults.endpoint,
   });
   config = {
     repository,
@@ -138,7 +138,7 @@ export async function initRepo({
     throw err;
   }
 
-  const { hostname } = URL.parse(endpoint_);
+  const { hostname } = URL.parse(defaults.endpoint);
 
   // Converts API hostnames to their respective HTTP git hosts:
   // `api.bitbucket.org`  to `bitbucket.org`
@@ -531,7 +531,9 @@ export async function ensureIssue({
   } catch (err) /* istanbul ignore next */ {
     if (err.message.startsWith('Repository has no issue tracker.')) {
       logger.debug(
-        `Issues are disabled, so could not create issue: ${err.message}`
+        `Issues are disabled, so could not create issue: ${
+          err.message as string
+        }`
       );
     } else {
       logger.warn({ err }, 'Could not ensure issue');
@@ -596,7 +598,7 @@ export async function addReviewers(
   prId: number,
   reviewers: string[]
 ): Promise<void> {
-  logger.debug(`Adding reviewers ${reviewers} to #${prId}`);
+  logger.debug(`Adding reviewers '${reviewers.join(', ')}' to #${prId}`);
 
   const { title } = await getPr(prId);
 
diff --git a/lib/platform/gitea/index.ts b/lib/platform/gitea/index.ts
index 574c7423a57c6d88d8d61f4bd1d48d53dbc3a35b..8ee46283700f148fc162b276d6f95290a4707c5f 100644
--- a/lib/platform/gitea/index.ts
+++ b/lib/platform/gitea/index.ts
@@ -49,7 +49,7 @@ interface GiteaRepoConfig {
   labelList: Promise<helper.Label[]> | null;
 }
 
-const defaults: any = {
+const defaults = {
   hostType: PLATFORM_TYPE_GITEA,
   endpoint: 'https://gitea.com/api/v1/',
 };
@@ -796,7 +796,9 @@ const platform: Platform = {
   },
 
   async addAssignees(number: number, assignees: string[]): Promise<void> {
-    logger.debug(`Updating assignees ${assignees} on Issue #${number}`);
+    logger.debug(
+      `Updating assignees '${assignees?.join(', ')}' on Issue #${number}`
+    );
     await helper.updateIssue(config.repository, number, {
       assignees,
     });
@@ -805,7 +807,9 @@ const platform: Platform = {
   addReviewers(number: number, reviewers: string[]): Promise<void> {
     // Adding reviewers to a PR through API is not supported by Gitea as of today
     // See tracking issue: https://github.com/go-gitea/gitea/issues/5733
-    logger.debug(`Updating reviewers ${reviewers} on Pull Request #${number}`);
+    logger.debug(
+      `Updating reviewers '${reviewers?.join(', ')}' on Pull Request #${number}`
+    );
     logger.warn('Unimplemented in Gitea: Reviewers');
     return Promise.resolve();
   },
diff --git a/lib/platform/github/index.ts b/lib/platform/github/index.ts
index 8138ac1cc541842f59b556441f2b79aeacab2f29..cc110fa87e0b26eca87aff204258c1e1a37cab65 100644
--- a/lib/platform/github/index.ts
+++ b/lib/platform/github/index.ts
@@ -47,8 +47,10 @@ import {
   CombinedBranchStatus,
   Comment,
   GhBranchStatus,
+  GhGraphQlPr,
   GhPr,
   GhRepo,
+  GhRestPr,
   LocalRepoConfig,
   PrList,
 } from './types';
@@ -498,9 +500,13 @@ async function getClosedPrs(): Promise<PrList> {
         }
       }
       `;
-      const nodes = await githubApi.queryRepoField<any>(query, 'pullRequests', {
-        paginate: false,
-      });
+      const nodes = await githubApi.queryRepoField<GhGraphQlPr>(
+        query,
+        'pullRequests',
+        {
+          paginate: false,
+        }
+      );
       const prNumbers: number[] = [];
       // istanbul ignore if
       if (!nodes?.length) {
@@ -513,12 +519,10 @@ async function getClosedPrs(): Promise<PrList> {
         pr.state = pr.state.toLowerCase();
         pr.branchName = pr.headRefName;
         delete pr.headRefName;
-        pr.comments = pr.comments.nodes.map(
-          (comment: { databaseId: number; body: string }) => ({
-            id: comment.databaseId,
-            body: comment.body,
-          })
-        );
+        pr.comments = pr.comments.nodes.map((comment) => ({
+          id: comment.databaseId,
+          body: comment.body,
+        }));
         pr.body = 'dummy body'; // just in case
         config.closedPrList[pr.number] = pr;
         prNumbers.push(pr.number);
@@ -598,10 +602,14 @@ async function getOpenPrs(): Promise<PrList> {
         }
       }
       `;
-      const nodes = await githubApi.queryRepoField<any>(query, 'pullRequests', {
-        paginate: false,
-        acceptHeader: 'application/vnd.github.merge-info-preview+json',
-      });
+      const nodes = await githubApi.queryRepoField<GhGraphQlPr>(
+        query,
+        'pullRequests',
+        {
+          paginate: false,
+          acceptHeader: 'application/vnd.github.merge-info-preview+json',
+        }
+      );
       const prNumbers: number[] = [];
       // istanbul ignore if
       if (!nodes?.length) {
@@ -636,9 +644,7 @@ async function getOpenPrs(): Promise<PrList> {
           pr.isConflicted = false;
         }
         if (pr.labels) {
-          pr.labels = pr.labels.nodes.map(
-            (label: { name: string }) => label.name
-          );
+          pr.labels = pr.labels.nodes.map((label) => label.name);
         }
         pr.hasAssignees = !!(pr.assignees?.totalCount > 0);
         delete pr.assignees;
@@ -681,7 +687,7 @@ export async function getPr(prNo: number): Promise<Pr | null> {
     'PR not found in open or closed PRs list - trying to fetch it directly'
   );
   const pr = (
-    await githubApi.getJson<any>(
+    await githubApi.getJson<GhRestPr>(
       `repos/${config.parentRepo || config.repository}/pulls/${prNo}`
     )
   ).body;
@@ -805,7 +811,7 @@ async function getStatus(
 // Returns the combined status for a branch.
 export async function getBranchStatus(
   branchName: string,
-  requiredStatusChecks: any
+  requiredStatusChecks: any[] | undefined
 ): Promise<BranchStatus> {
   logger.debug(`getBranchStatus(${branchName})`);
   if (!requiredStatusChecks) {
@@ -1036,7 +1042,7 @@ export async function findIssue(title: string): Promise<Issue | null> {
   if (!issue) {
     return null;
   }
-  logger.debug('Found issue ' + issue.number);
+  logger.debug(`Found issue ${issue.number}`);
   const issueBody = (
     await githubApi.getJson<{ body: string }>(
       `repos/${config.parentRepo || config.repository}/issues/${issue.number}`
@@ -1090,7 +1096,7 @@ export async function ensureIssue({
       }
       for (const i of issues) {
         if (i.state === 'open' && i.number !== issue.number) {
-          logger.warn('Closing duplicate issue ' + i.number);
+          logger.warn(`Closing duplicate issue ${i.number}`);
           await closeIssue(i.number);
         }
       }
@@ -1139,7 +1145,9 @@ export async function ensureIssue({
   } catch (err) /* istanbul ignore next */ {
     if (err.body?.message?.startsWith('Issues are disabled for this repo')) {
       logger.debug(
-        `Issues are disabled, so could not create issue: ${err.message}`
+        `Issues are disabled, so could not create issue: ${
+          (err as Error).message
+        }`
       );
     } else {
       logger.warn({ err }, 'Could not ensure issue');
@@ -1163,7 +1171,7 @@ export async function addAssignees(
   issueNo: number,
   assignees: string[]
 ): Promise<void> {
-  logger.debug(`Adding assignees ${assignees} to #${issueNo}`);
+  logger.debug(`Adding assignees '${assignees.join(', ')}' to #${issueNo}`);
   const repository = config.parentRepo || config.repository;
   await githubApi.postJson(`repos/${repository}/issues/${issueNo}/assignees`, {
     body: {
@@ -1176,7 +1184,7 @@ export async function addReviewers(
   prNo: number,
   reviewers: string[]
 ): Promise<void> {
-  logger.debug(`Adding reviewers ${reviewers} to #${prNo}`);
+  logger.debug(`Adding reviewers '${reviewers.join(', ')}' to #${prNo}`);
 
   const userReviewers = reviewers.filter((e) => !e.startsWith('team:'));
   const teamReviewers = reviewers
@@ -1203,7 +1211,7 @@ async function addLabels(
   issueNo: number,
   labels: string[] | null
 ): Promise<void> {
-  logger.debug(`Adding labels ${labels} to #${issueNo}`);
+  logger.debug(`Adding labels '${labels?.join(', ')}' to #${issueNo}`);
   const repository = config.parentRepo || config.repository;
   if (is.array(labels) && labels.length) {
     await githubApi.postJson(`repos/${repository}/issues/${issueNo}/labels`, {
@@ -1507,7 +1515,7 @@ export async function mergePr(
     config.parentRepo || config.repository
   }/pulls/${prNo}/merge`;
   const options = {
-    body: {} as any,
+    body: {} as { merge_method?: string },
   };
   let automerged = false;
   if (config.mergeMethod) {
@@ -1611,16 +1619,14 @@ export async function getVulnerabilityAlerts(): Promise<VulnerabilityAlert[]> {
       }
     }
   }`;
-  let alerts = [];
+  let alerts: VulnerabilityAlert[] = [];
   try {
-    const vulnerabilityAlerts = await githubApi.queryRepoField<{ node: any }>(
-      query,
-      'vulnerabilityAlerts',
-      {
-        paginate: false,
-        acceptHeader: 'application/vnd.github.vixen-preview+json',
-      }
-    );
+    const vulnerabilityAlerts = await githubApi.queryRepoField<{
+      node: VulnerabilityAlert;
+    }>(query, 'vulnerabilityAlerts', {
+      paginate: false,
+      acceptHeader: 'application/vnd.github.vixen-preview+json',
+    });
     if (vulnerabilityAlerts?.length) {
       alerts = vulnerabilityAlerts.map((edge) => edge.node);
       if (alerts.length) {
diff --git a/lib/platform/github/types.ts b/lib/platform/github/types.ts
index 04cb7785e75ad414a6d867f6fe305ac2aabc513d..d2e868c1f19c89956f3aca204990279bb03b4ed0 100644
--- a/lib/platform/github/types.ts
+++ b/lib/platform/github/types.ts
@@ -22,6 +22,24 @@ export interface Comment {
 
 export interface GhPr extends Pr {
   comments: Comment[];
+  mergeable: boolean;
+}
+
+export interface GhRestPr extends GhPr {
+  head: { ref: string; sha: string };
+  mergeable_state: string;
+}
+
+export interface GhGraphQlPr extends GhPr {
+  commits: any;
+  reviewRequests: any;
+  assignees: any;
+  mergeStateStatus: string;
+  reviews: any;
+  baseRefName: string;
+  headRefName: string;
+  comments: Comment[] & { nodes?: { databaseId: number; body: string }[] };
+  labels: string[] & { nodes?: { name: string }[] };
 }
 
 export interface LocalRepoConfig {
diff --git a/lib/platform/gitlab/index.ts b/lib/platform/gitlab/index.ts
index 3308fab02d3c66eab3bd0e29d95912800d765321..d069d8a0ccb48557325ff2d6fe7fbccf30d86e4c 100644
--- a/lib/platform/gitlab/index.ts
+++ b/lib/platform/gitlab/index.ts
@@ -1,6 +1,5 @@
 import URL, { URLSearchParams } from 'url';
 import is from '@sindresorhus/is';
-
 import delay from 'delay';
 import { configFileNames } from '../../config/app-strings';
 import { RenovateConfig } from '../../config/common';
@@ -40,29 +39,17 @@ import {
   VulnerabilityAlert,
 } from '../common';
 import { smartTruncate } from '../utils/pr-body';
+import { GitlabComment, GitlabIssue, MergeMethod, RepoResponse } from './types';
 
 const gitlabApi = new GitlabHttp();
 
-type MergeMethod = 'merge' | 'rebase_merge' | 'ff';
-type RepoResponse = {
-  archived: boolean;
-  mirror: boolean;
-  default_branch: string;
-  empty_repo: boolean;
-  http_url_to_repo: string;
-  forked_from_project: boolean;
-  repository_access_level: 'disabled' | 'private' | 'enabled';
-  merge_requests_access_level: 'disabled' | 'private' | 'enabled';
-  merge_method: MergeMethod;
-  path_with_namespace: string;
-};
 const defaultConfigFile = configFileNames[0];
 let config: {
   repository: string;
   localDir: string;
   email: string;
   prList: any[];
-  issueList: any[];
+  issueList: GitlabIssue[];
   optimizeForDisabled: boolean;
   mergeMethod: MergeMethod;
 } = {} as any;
@@ -610,7 +597,7 @@ export async function setBranchStatus({
 
 // Issue
 
-export async function getIssueList(): Promise<any[]> {
+export async function getIssueList(): Promise<GitlabIssue[]> {
   if (!config.issueList) {
     const query = new URLSearchParams({
       per_page: '100',
@@ -641,7 +628,7 @@ export async function findIssue(title: string): Promise<Issue | null> {
   logger.debug(`findIssue(${title})`);
   try {
     const issueList = await getIssueList();
-    const issue = issueList.find((i: { title: string }) => i.title === title);
+    const issue = issueList.find((i) => i.title === title);
     if (!issue) {
       return null;
     }
@@ -669,9 +656,9 @@ export async function ensureIssue({
   const description = getPrBody(sanitize(body));
   try {
     const issueList = await getIssueList();
-    let issue = issueList.find((i: { title: string }) => i.title === title);
+    let issue = issueList.find((i) => i.title === title);
     if (!issue) {
-      issue = issueList.find((i: { title: string }) => i.title === reuseTitle);
+      issue = issueList.find((i) => i.title === reuseTitle);
     }
     if (issue) {
       const existingDescription = (
@@ -703,7 +690,7 @@ export async function ensureIssue({
     }
   } catch (err) /* istanbul ignore next */ {
     if (err.message.startsWith('Issues are disabled for this repo')) {
-      logger.debug(`Could not create issue: ${err.message}`);
+      logger.debug(`Could not create issue: ${(err as Error).message}`);
     } else {
       logger.warn({ err }, 'Could not ensure issue');
     }
@@ -731,7 +718,7 @@ export async function addAssignees(
   iid: number,
   assignees: string[]
 ): Promise<void> {
-  logger.debug(`Adding assignees ${assignees} to #${iid}`);
+  logger.debug(`Adding assignees '${assignees.join(', ')}' to #${iid}`);
   try {
     let assigneeId = (
       await gitlabApi.getJson<{ id: number }[]>(
@@ -763,7 +750,7 @@ export async function addAssignees(
 }
 
 export function addReviewers(iid: number, reviewers: string[]): Promise<void> {
-  logger.debug(`addReviewers('${iid}, '${reviewers})`);
+  logger.debug(`addReviewers('${iid}, [${reviewers.join(', ')}])`);
   logger.warn('Unimplemented in GitLab: approvals');
   return Promise.resolve();
 }
@@ -883,11 +870,6 @@ export async function ensureComment({
   return true;
 }
 
-type GitlabComment = {
-  body: string;
-  id: number;
-};
-
 export async function ensureCommentRemoval({
   number: issueNo,
   topic,
diff --git a/lib/platform/gitlab/types.ts b/lib/platform/gitlab/types.ts
new file mode 100644
index 0000000000000000000000000000000000000000..fda51a9ffcf971f962233184b70dad01c6c7992e
--- /dev/null
+++ b/lib/platform/gitlab/types.ts
@@ -0,0 +1,24 @@
+export interface GitlabIssue {
+  iid: number;
+  title: string;
+}
+
+export interface GitlabComment {
+  body: string;
+  id: number;
+}
+
+export type MergeMethod = 'merge' | 'rebase_merge' | 'ff';
+
+export type RepoResponse = {
+  archived: boolean;
+  mirror: boolean;
+  default_branch: string;
+  empty_repo: boolean;
+  http_url_to_repo: string;
+  forked_from_project: boolean;
+  repository_access_level: 'disabled' | 'private' | 'enabled';
+  merge_requests_access_level: 'disabled' | 'private' | 'enabled';
+  merge_method: MergeMethod;
+  path_with_namespace: string;
+};
diff --git a/lib/types/git.ts b/lib/types/git.ts
index 073995d025d4e5ef602759ef3710e7a46a0d1cbe..e6e25ed4cd70c606d4b4e179deb9e2598a869415 100644
--- a/lib/types/git.ts
+++ b/lib/types/git.ts
@@ -3,3 +3,7 @@ export type GitTreeNode = {
   path: string;
   mode: string;
 };
+
+export type GitProtocol = 'ssh' | 'http' | 'https';
+
+export type GitOptions = Record<string, null | string | number>;
diff --git a/lib/util/git/index.ts b/lib/util/git/index.ts
index 8b6be5db69781bad1316f9ab17e5c968e065f121..12af27d96f4d367bd58e9d2dccbafbeee22f6271 100644
--- a/lib/util/git/index.ts
+++ b/lib/util/git/index.ts
@@ -3,7 +3,6 @@ import URL from 'url';
 import fs from 'fs-extra';
 import Git, {
   DiffResult as DiffResult_,
-  Options,
   ResetMode,
   SimpleGit,
   StatusResult as StatusResult_,
@@ -17,6 +16,7 @@ import {
 } from '../../constants/error-messages';
 import { logger } from '../../logger';
 import { ExternalHostError } from '../../types/errors/external-host-error';
+import { GitOptions, GitProtocol } from '../../types/git';
 import * as limits from '../../workers/global/limits';
 import { writePrivateKey } from './private-key';
 
@@ -34,7 +34,7 @@ interface StorageConfig {
   localDir: string;
   currentBranch?: string;
   url: string;
-  extraCloneOpts?: Options;
+  extraCloneOpts?: GitOptions;
   gitAuthorName?: string;
   gitAuthorEmail?: string;
 }
@@ -214,10 +214,10 @@ export async function syncGit(): Promise<void> {
     const cloneStart = Date.now();
     try {
       // clone only the default branch
-      let opts = ['--depth=2'];
+      const opts = ['--depth=2'];
       if (config.extraCloneOpts) {
-        opts = opts.concat(
-          Object.entries(config.extraCloneOpts).map((e) => `${e[0]}=${e[1]}`)
+        opts.push(
+          ...Object.entries(config.extraCloneOpts).map((e) => `${e[0]}=${e[1]}`)
         );
       }
       await git.clone(config.url, '.', opts);
@@ -661,7 +661,7 @@ export function getUrl({
   host,
   repository,
 }: {
-  protocol?: 'ssh' | 'http' | 'https';
+  protocol?: GitProtocol;
   auth?: string;
   hostname?: string;
   host?: string;
diff --git a/lib/workers/common.ts b/lib/workers/common.ts
index 7ba92997535b56f789f5ed33ef686f302ba5d9b6..b0673050a363b2b42789e392bde2a876d2aa842e 100644
--- a/lib/workers/common.ts
+++ b/lib/workers/common.ts
@@ -1,3 +1,4 @@
+import type { Merge } from 'type-fest';
 import {
   GroupConfig,
   RenovateAdminConfig,
@@ -15,7 +16,6 @@ import {
 import { PlatformPrOptions } from '../platform';
 import { File } from '../util/git';
 import { ChangeLogResult } from './pr/changelog/common';
-import { Merge } from 'type-fest';
 
 export interface BranchUpgradeConfig
   extends Merge<RenovateConfig, PackageDependency>,
diff --git a/lib/workers/repository/updates/branchify.ts b/lib/workers/repository/updates/branchify.ts
index 2d2320e7030d8fc88108ba74f51fd6f382801889..f3d20fd4d51d9a9a2862034600c3fee261486c34 100644
--- a/lib/workers/repository/updates/branchify.ts
+++ b/lib/workers/repository/updates/branchify.ts
@@ -1,5 +1,6 @@
 import { clean as cleanGitRef } from 'clean-git-ref';
 import slugify from 'slugify';
+import type { Merge } from 'type-fest';
 import { RenovateConfig, ValidationMessage } from '../../../config';
 import { addMeta, logger, removeMeta } from '../../../logger';
 import * as template from '../../../util/template';
@@ -7,7 +8,6 @@ import { BranchConfig, BranchUpgradeConfig } from '../../common';
 import { embedChangelogs } from '../changelog';
 import { flattenUpdates } from './flatten';
 import { generateBranchConfig } from './generate';
-import { Merge } from 'type-fest';
 
 /**
  * Clean git branch name
diff --git a/test/httpMock.ts b/test/httpMock.ts
index dfc1295284c4b15e8f4c4044238e7b1be1d05f80..1317d730986f95e1a4126186e40d626483a8a94d 100644
--- a/test/httpMock.ts
+++ b/test/httpMock.ts
@@ -82,7 +82,12 @@ function simplifyGraphqlAST(tree: any): any {
   return tree;
 }
 
-function onMissing(req: any, opts: any): void /* istanbul ignore next */ {
+type TestRequest = {
+  method: string;
+  href: string;
+};
+
+function onMissing(req: TestRequest, opts?: TestRequest): void {
   if (!opts) {
     missingLog.push(`  ${req.method} ${req.href}`);
   } else {
diff --git a/test/newline-snapshot-serializer.ts b/test/newline-snapshot-serializer.ts
index 86d72856e0e4c59159449fbbe1721e05739c213f..adbd6741563406276668149150d5cd586b8e87b6 100644
--- a/test/newline-snapshot-serializer.ts
+++ b/test/newline-snapshot-serializer.ts
@@ -1,9 +1,11 @@
 let prev: string;
 
-export function print(val: any): string {
+// this does not work as intended
+// see https://jestjs.io/docs/en/configuration#snapshotserializers-arraystring
+export function print(val: string): string {
   return JSON.stringify(val);
 }
-export function test(val: any): boolean {
+export function test(val: string): boolean {
   if (['prBody', 'prTitle'].some((str) => str === prev)) {
     return typeof val === 'string' && val.includes('\n');
   }
diff --git a/test/util.ts b/test/util.ts
index 8b4b2170345ea5d2e6525b7a247cdf7bc1438f05..4dd6fe791c3aefd6486ce25443037de4df8903c0 100644
--- a/test/util.ts
+++ b/test/util.ts
@@ -1,5 +1,5 @@
 import crypto from 'crypto';
-import { expect, jest } from '@jest/globals';
+import { expect } from '@jest/globals';
 import { RenovateConfig as _RenovateConfig } from '../lib/config';
 import { getConfig } from '../lib/config/defaults';
 import { platform as _platform } from '../lib/platform';
diff --git a/test/website-docs.spec.ts b/test/website-docs.spec.ts
index b199495d11a831f048a536050e910d9b69f2eb6f..68c9bbaf17d491054163b73820f47e1dba31ebc6 100644
--- a/test/website-docs.spec.ts
+++ b/test/website-docs.spec.ts
@@ -84,7 +84,7 @@ describe('docs', () => {
     on an error, it throws a custom message.
   */
   expect.extend({
-    toContainOption<T>(received: T[], argument: T) {
+    toContainOption<T extends string>(received: T[], argument: T) {
       if (received.includes(argument)) {
         return {
           message: (): string =>
diff --git a/tools/eslint-gh-reporter.ts b/tools/eslint-gh-reporter.ts
index b11e65dbb7bf031abe9fc0d19a45362123cb8367..4d9ab6452770a578779d59569c8263bb134ab764 100644
--- a/tools/eslint-gh-reporter.ts
+++ b/tools/eslint-gh-reporter.ts
@@ -38,7 +38,7 @@ const formatter: CLIEngine.Formatter = (results) => {
       }
     }
   } catch (e) {
-    error(`Unexpected error: ${e}`);
+    error(`Unexpected error: ${(e as Error).toString()}`);
   }
   return '';
 };
diff --git a/tools/jest-gh-reporter.ts b/tools/jest-gh-reporter.ts
index de6c33b362e5dda001c080fbad78df127237ffbb..3112a8c88364199dcbbb37c13aa6b3d354235643 100644
--- a/tools/jest-gh-reporter.ts
+++ b/tools/jest-gh-reporter.ts
@@ -67,7 +67,7 @@ class GitHubReporter extends BaseReporter {
         }
       }
     } catch (e) {
-      error(`Unexpected error: ${e}`);
+      error(`Unexpected error: ${(e as Error).toString()}`);
     }
   }
 }
diff --git a/tsconfig.json b/tsconfig.json
index 3f4ddb1d0689c2d64a2ffa63304aaf42b82f98b4..78c477e57071f45ea79fd767ec074cbf4509039a 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -11,6 +11,7 @@
     "esModuleInterop": true,
     "resolveJsonModule": false,
     "isolatedModules": true,
+    "noUnusedLocals": true,
     "lib": ["es2018"],
     "types": ["node", "jest"],
     "allowJs": true,