From 5723be1ac5c776cff5c549457bd3cc238c54b5cf Mon Sep 17 00:00:00 2001
From: Michael Kriese <michael.kriese@visualon.de>
Date: Fri, 11 Feb 2022 11:02:30 +0100
Subject: [PATCH] refactor: add strict null checks (#14169)

---
 lib/config/decrypt.ts                         |  6 +-
 lib/config/presets/internal/index.ts          |  2 +-
 lib/config/secrets.ts                         | 11 ++-
 lib/config/types.ts                           |  2 +-
 lib/platform/bitbucket-server/types.ts        | 12 ++++
 lib/platform/bitbucket-server/utils.spec.ts   | 38 ++++++++++
 lib/platform/bitbucket-server/utils.ts        | 27 +++----
 lib/platform/bitbucket/comments.ts            |  2 +-
 lib/platform/github/massage-markdown-links.ts | 10 +--
 lib/platform/github/user.ts                   |  4 +-
 lib/platform/types.ts                         |  4 +-
 lib/types/base.ts                             |  1 +
 lib/types/index.ts                            |  2 +
 lib/util/git/auth.ts                          |  8 ++-
 lib/util/git/author.ts                        | 10 +--
 lib/util/git/types.ts                         |  2 +-
 lib/util/ignore.ts                            |  2 +-
 lib/util/index.spec.ts                        |  2 +-
 lib/util/merge-confidence/index.ts            | 25 +++----
 lib/workers/branch/automerge.ts               |  2 +-
 lib/workers/branch/index.spec.ts              | 13 +++-
 lib/workers/branch/reuse.spec.ts              |  1 +
 lib/workers/branch/schedule.ts                | 30 ++++----
 lib/workers/global/config/parse/cli.spec.ts   |  6 +-
 lib/workers/global/config/parse/cli.ts        |  5 +-
 lib/workers/global/config/parse/env.spec.ts   |  8 +--
 lib/workers/global/config/parse/env.ts        | 16 +++--
 lib/workers/global/config/parse/file.ts       |  2 +-
 lib/workers/global/config/parse/types.ts      |  4 ++
 lib/workers/pr/body/updates-table.ts          |  6 +-
 .../repository/extract/file-match.spec.ts     |  2 +-
 lib/workers/repository/finalise/prune.spec.ts |  3 -
 lib/workers/repository/result.ts              |  8 +--
 lib/workers/repository/stats.ts               |  5 ++
 tools/docs/config.ts                          |  5 +-
 tsconfig.strict.json                          | 71 +------------------
 36 files changed, 177 insertions(+), 180 deletions(-)
 create mode 100644 lib/platform/bitbucket-server/utils.spec.ts
 create mode 100644 lib/workers/global/config/parse/types.ts

diff --git a/lib/config/decrypt.ts b/lib/config/decrypt.ts
index 550e503800..65099de4aa 100644
--- a/lib/config/decrypt.ts
+++ b/lib/config/decrypt.ts
@@ -49,7 +49,7 @@ export function tryDecryptPublicKeyDefault(
   privateKey: string,
   encryptedStr: string
 ): string | null {
-  let decryptedStr: string = null;
+  let decryptedStr: string | null = null;
   try {
     decryptedStr = crypto
       .privateDecrypt(privateKey, Buffer.from(encryptedStr, 'base64'))
@@ -65,7 +65,7 @@ export function tryDecryptPublicKeyPKCS1(
   privateKey: string,
   encryptedStr: string
 ): string | null {
-  let decryptedStr: string = null;
+  let decryptedStr: string | null = null;
   try {
     decryptedStr = crypto
       .privateDecrypt(
@@ -87,7 +87,7 @@ export async function tryDecrypt(
   encryptedStr: string,
   repository: string
 ): Promise<string | null> {
-  let decryptedStr: string = null;
+  let decryptedStr: string | null = null;
   if (privateKey?.startsWith('-----BEGIN PGP PRIVATE KEY BLOCK-----')) {
     const decryptedObjStr = await tryDecryptPgp(privateKey, encryptedStr);
     if (decryptedObjStr) {
diff --git a/lib/config/presets/internal/index.ts b/lib/config/presets/internal/index.ts
index b0dd3dcbe4..e96253c9c3 100644
--- a/lib/config/presets/internal/index.ts
+++ b/lib/config/presets/internal/index.ts
@@ -35,7 +35,7 @@ export function getPreset({
   packageName: pkgName,
   presetName,
 }: PresetConfig): Preset | undefined {
-  return groups[pkgName]
+  return groups[pkgName] && presetName
     ? groups[pkgName][presetName]
     : /* istanbul ignore next */ undefined;
 }
diff --git a/lib/config/secrets.ts b/lib/config/secrets.ts
index e69df90e43..74d2be9878 100644
--- a/lib/config/secrets.ts
+++ b/lib/config/secrets.ts
@@ -102,17 +102,13 @@ function replaceSecretsinObject(
     if (is.array(value)) {
       for (const [arrayIndex, arrayItem] of value.entries()) {
         if (is.plainObject(arrayItem)) {
-          config[key][arrayIndex] = replaceSecretsinObject(
+          value[arrayIndex] = replaceSecretsinObject(
             arrayItem,
             secrets,
             deleteSecrets
           );
         } else if (is.string(arrayItem)) {
-          config[key][arrayIndex] = replaceSecretsInString(
-            key,
-            arrayItem,
-            secrets
-          );
+          value[arrayIndex] = replaceSecretsInString(key, arrayItem, secrets);
         }
       }
     }
@@ -131,5 +127,6 @@ export function applySecretsToConfig(
       addSecretForSanitizing(String(secret));
     }
   }
-  return replaceSecretsinObject(config, secrets, deleteSecrets);
+  // TODO: fix types (#9610)
+  return replaceSecretsinObject(config, secrets as never, deleteSecrets);
 }
diff --git a/lib/config/types.ts b/lib/config/types.ts
index 0beac92cdd..e792be3aa0 100644
--- a/lib/config/types.ts
+++ b/lib/config/types.ts
@@ -61,7 +61,7 @@ export interface RenovateSharedConfig {
   repositoryCache?: RepositoryCacheConfig;
   schedule?: string[];
   semanticCommits?: 'auto' | 'enabled' | 'disabled';
-  semanticCommitScope?: string;
+  semanticCommitScope?: string | null;
   semanticCommitType?: string;
   suppressNotifications?: string[];
   timezone?: string;
diff --git a/lib/platform/bitbucket-server/types.ts b/lib/platform/bitbucket-server/types.ts
index 9efb9caf76..fe5d4f2f74 100644
--- a/lib/platform/bitbucket-server/types.ts
+++ b/lib/platform/bitbucket-server/types.ts
@@ -1,3 +1,4 @@
+import type { HTTPError, Response } from 'got';
 import type { Pr } from '../types';
 
 export interface BbsConfig {
@@ -57,3 +58,14 @@ export interface BbsRestRepo {
 export interface BbsRestBranch {
   displayId: string;
 }
+
+export interface BitbucketErrorResponse {
+  errors?: {
+    exceptionName?: string;
+    reviewerErrors?: { context?: string }[];
+  }[];
+}
+
+export interface BitbucketError extends HTTPError {
+  readonly response: Response<BitbucketErrorResponse>;
+}
diff --git a/lib/platform/bitbucket-server/utils.spec.ts b/lib/platform/bitbucket-server/utils.spec.ts
new file mode 100644
index 0000000000..445f61a649
--- /dev/null
+++ b/lib/platform/bitbucket-server/utils.spec.ts
@@ -0,0 +1,38 @@
+import type { Response } from 'got';
+import { partial } from '../../../test/util';
+import type { BitbucketError, BitbucketErrorResponse } from './types';
+import {
+  BITBUCKET_INVALID_REVIEWERS_EXCEPTION,
+  getInvalidReviewers,
+} from './utils';
+
+describe('platform/bitbucket-server/utils', () => {
+  function createError(body: Partial<BitbucketErrorResponse> = undefined) {
+    return partial<BitbucketError>({
+      response: partial<Response<BitbucketErrorResponse>>({ body }),
+    });
+  }
+
+  it('getInvalidReviewers', () => {
+    expect(
+      getInvalidReviewers(
+        createError({
+          errors: [
+            {
+              exceptionName: BITBUCKET_INVALID_REVIEWERS_EXCEPTION,
+              reviewerErrors: [{ context: 'dummy' }, {}],
+            },
+          ],
+        })
+      )
+    ).toStrictEqual(['dummy']);
+    expect(getInvalidReviewers(createError())).toStrictEqual([]);
+    expect(
+      getInvalidReviewers(
+        createError({
+          errors: [{ exceptionName: BITBUCKET_INVALID_REVIEWERS_EXCEPTION }],
+        })
+      )
+    ).toStrictEqual([]);
+  });
+});
diff --git a/lib/platform/bitbucket-server/utils.ts b/lib/platform/bitbucket-server/utils.ts
index cc248e8896..ba35cf8ebd 100644
--- a/lib/platform/bitbucket-server/utils.ts
+++ b/lib/platform/bitbucket-server/utils.ts
@@ -1,6 +1,6 @@
 // SEE for the reference https://github.com/renovatebot/renovate/blob/c3e9e572b225085448d94aa121c7ec81c14d3955/lib/platform/bitbucket/utils.js
 import url from 'url';
-import type { HTTPError, Response } from 'got';
+import is from '@sindresorhus/is';
 import { PrState } from '../../types';
 import type {
   HttpOptions,
@@ -8,9 +8,9 @@ import type {
   HttpResponse,
 } from '../../util/http';
 import { BitbucketServerHttp } from '../../util/http/bitbucket-server';
-import type { BbsPr, BbsRestPr } from './types';
+import type { BbsPr, BbsRestPr, BitbucketError } from './types';
 
-const BITBUCKET_INVALID_REVIEWERS_EXCEPTION =
+export const BITBUCKET_INVALID_REVIEWERS_EXCEPTION =
   'com.atlassian.bitbucket.pull.InvalidPullRequestReviewersException';
 
 const bitbucketServerHttp = new BitbucketServerHttp();
@@ -127,19 +127,8 @@ export interface BitbucketStatus {
   state: BitbucketBranchState;
 }
 
-interface BitbucketErrorResponse {
-  errors?: {
-    exceptionName?: string;
-    reviewerErrors?: { context?: string }[];
-  }[];
-}
-
-interface BitbucketError extends HTTPError {
-  readonly response: Response<BitbucketErrorResponse>;
-}
-
 export function isInvalidReviewersResponse(err: BitbucketError): boolean {
-  const errors = err?.response?.body?.errors || [];
+  const errors = err?.response?.body?.errors ?? [];
   return (
     errors.length > 0 &&
     errors.every(
@@ -149,12 +138,14 @@ export function isInvalidReviewersResponse(err: BitbucketError): boolean {
 }
 
 export function getInvalidReviewers(err: BitbucketError): string[] {
-  const errors = err?.response?.body?.errors || [];
-  let invalidReviewers = [];
+  const errors = err?.response?.body?.errors ?? [];
+  let invalidReviewers: string[] = [];
   for (const error of errors) {
     if (error.exceptionName === BITBUCKET_INVALID_REVIEWERS_EXCEPTION) {
       invalidReviewers = invalidReviewers.concat(
-        error.reviewerErrors?.map(({ context }) => context) || []
+        error.reviewerErrors
+          ?.map(({ context }) => context)
+          .filter(is.nonEmptyString) ?? []
       );
     }
   }
diff --git a/lib/platform/bitbucket/comments.ts b/lib/platform/bitbucket/comments.ts
index 159ed3e109..afce16d4dc 100644
--- a/lib/platform/bitbucket/comments.ts
+++ b/lib/platform/bitbucket/comments.ts
@@ -131,7 +131,7 @@ export async function ensureCommentRemoval(
     const byContent = (comment: Comment): boolean =>
       comment.content.raw.trim() === content;
 
-    let commentId: number | null = null;
+    let commentId: number | undefined = undefined;
 
     if (topic) {
       commentId = comments.find(byTopic)?.id;
diff --git a/lib/platform/github/massage-markdown-links.ts b/lib/platform/github/massage-markdown-links.ts
index 84391ff764..01249af34e 100644
--- a/lib/platform/github/massage-markdown-links.ts
+++ b/lib/platform/github/massage-markdown-links.ts
@@ -20,8 +20,8 @@ function massageLink(input: string): string {
 
 function collectLinkPosition(input: string, matches: UrlMatch[]): Plugin {
   const transformer = (tree: Content): void => {
-    const startOffset: number = tree.position.start.offset;
-    const endOffset: number = tree.position.end.offset;
+    const startOffset: number = tree.position?.start.offset ?? 0;
+    const endOffset: number = tree.position?.end.offset ?? 0;
 
     if (tree.type === 'link') {
       const substr = input.slice(startOffset, endOffset);
@@ -39,7 +39,7 @@ function collectLinkPosition(input: string, matches: UrlMatch[]): Plugin {
       const urlMatches = [...tree.value.matchAll(globalUrlReg)];
       for (const match of urlMatches) {
         const [url] = match;
-        const start = startOffset + match.index;
+        const start = startOffset + (match.index ?? 0);
         const end = start + url.length;
         const newUrl = massageLink(url);
         matches.push({ start, end, replaceTo: `[${url}](${newUrl})` });
@@ -56,7 +56,7 @@ function collectLinkPosition(input: string, matches: UrlMatch[]): Plugin {
 
 export function massageMarkdownLinks(content: string): string {
   try {
-    const rightSpaces = content.replace(content.trimRight(), '');
+    const rightSpaces = content.replace(content.trimEnd(), '');
     const matches: UrlMatch[] = [];
     remark().use(collectLinkPosition(content, matches)).processSync(content);
     const result = matches.reduceRight((acc, { start, end, replaceTo }) => {
@@ -64,7 +64,7 @@ export function massageMarkdownLinks(content: string): string {
       const rightPart = acc.slice(end);
       return leftPart + replaceTo + rightPart;
     }, content);
-    return result.trimRight() + rightSpaces;
+    return result.trimEnd() + rightSpaces;
   } catch (err) /* istanbul ignore next */ {
     logger.warn({ err }, `Unable to massage markdown text`);
     return content;
diff --git a/lib/platform/github/user.ts b/lib/platform/github/user.ts
index fb7f2a08d1..24a5b93f6c 100644
--- a/lib/platform/github/user.ts
+++ b/lib/platform/github/user.ts
@@ -37,7 +37,7 @@ export async function getUserDetails(
   }
 }
 
-let userEmail: string;
+let userEmail: string | null;
 
 export async function getUserEmail(
   endpoint: string,
@@ -49,7 +49,7 @@ export async function getUserEmail(
         token,
       })
     ).body;
-    userEmail = emails?.[0].email || null;
+    userEmail = emails?.[0].email ?? null;
     return userEmail;
   } catch (err) {
     logger.debug(
diff --git a/lib/platform/types.ts b/lib/platform/types.ts
index 6a65a04d8f..581e779041 100644
--- a/lib/platform/types.ts
+++ b/lib/platform/types.ts
@@ -56,7 +56,7 @@ export interface Pr {
   hasAssignees?: boolean;
   hasReviewers?: boolean;
   labels?: string[];
-  number?: number;
+  number: number;
   reviewers?: string[];
   sha?: string;
   sourceRepo?: string;
@@ -127,7 +127,7 @@ export interface MergePRConfig {
 }
 export interface EnsureCommentConfig {
   number: number;
-  topic: string;
+  topic: string | null;
   content: string;
 }
 
diff --git a/lib/types/base.ts b/lib/types/base.ts
index 4fa51018b8..daa6d1f03c 100644
--- a/lib/types/base.ts
+++ b/lib/types/base.ts
@@ -7,4 +7,5 @@ export interface ModuleApi {
 
 export type RenovatePackageJson = PackageJson & {
   'engines-next': Record<string, string>;
+  version: string;
 };
diff --git a/lib/types/index.ts b/lib/types/index.ts
index f85b78de34..a447eb060e 100644
--- a/lib/types/index.ts
+++ b/lib/types/index.ts
@@ -5,3 +5,5 @@ export * from './branch-status';
 export * from './vulnerability-alert';
 export * from './pr-state';
 export * from './base';
+
+export type AutoMergeType = 'branch' | 'pr' | 'pr-comment';
diff --git a/lib/util/git/auth.ts b/lib/util/git/auth.ts
index 512a1ebe17..dcc5d4e253 100644
--- a/lib/util/git/auth.ts
+++ b/lib/util/git/auth.ts
@@ -23,14 +23,16 @@ export function getGitAuthenticatedEnvironmentVariables(
 
   // check if the environmentVariables already contain a GIT_CONFIG_COUNT or if the process has one
   const gitConfigCountEnvVariable =
-    environmentVariables?.GIT_CONFIG_COUNT || process.env.GIT_CONFIG_COUNT;
+    environmentVariables?.GIT_CONFIG_COUNT ?? process.env.GIT_CONFIG_COUNT;
   let gitConfigCount = 0;
   if (gitConfigCountEnvVariable) {
     // passthrough the gitConfigCountEnvVariable environment variable as start value of the index count
     gitConfigCount = parseInt(gitConfigCountEnvVariable, 10);
     if (Number.isNaN(gitConfigCount)) {
       logger.warn(
-        `Found GIT_CONFIG_COUNT env variable, but couldn't parse the value to an integer: ${process.env.GIT_CONFIG_COUNT}. Ignoring it.`
+        `Found GIT_CONFIG_COUNT env variable, but couldn't parse the value to an integer: ${String(
+          process.env.GIT_CONFIG_COUNT
+        )}. Ignoring it.`
       );
       gitConfigCount = 0;
     }
@@ -63,7 +65,7 @@ export function getGitAuthenticatedEnvironmentVariables(
 
 function getAuthenticationRulesWithToken(
   url: string,
-  hostType: string,
+  hostType: string | undefined,
   authToken: string
 ): AuthenticationRule[] {
   let token = authToken;
diff --git a/lib/util/git/author.ts b/lib/util/git/author.ts
index 26860683b6..8690538d6a 100644
--- a/lib/util/git/author.ts
+++ b/lib/util/git/author.ts
@@ -4,7 +4,7 @@ import { regEx } from '../regex';
 import type { GitAuthor } from './types';
 
 export function parseGitAuthor(input: string): GitAuthor | null {
-  let result: GitAuthor = null;
+  let result: GitAuthor | null = null;
   if (!input) {
     return null;
   }
@@ -13,7 +13,7 @@ export function parseGitAuthor(input: string): GitAuthor | null {
     if (result) {
       return result;
     }
-    let massagedInput;
+    let massagedInput: string | undefined;
     let massagedBotEmail = false;
     if (input.includes('<') && input.includes('>')) {
       // try wrapping the name part in quotations
@@ -21,7 +21,7 @@ export function parseGitAuthor(input: string): GitAuthor | null {
     }
     if (input.includes('[bot]@')) {
       // invalid github app/bot addresses
-      massagedInput = (massagedInput || input).replace('[bot]@', '@');
+      massagedInput = (massagedInput ?? input).replace('[bot]@', '@');
       massagedBotEmail = true;
     }
     if (!massagedInput) {
@@ -30,11 +30,11 @@ export function parseGitAuthor(input: string): GitAuthor | null {
     const parsed = addrs.parseOneAddress(massagedInput) as addrs.ParsedMailbox;
     if (parsed?.address) {
       result = {
-        name: parsed.name || input.replace(regEx(/@.*/), ''),
+        name: parsed.name ?? input.replace(regEx(/@.*/), ''),
         address: parsed.address,
       };
       if (massagedBotEmail) {
-        result.address = result.address.replace('@', '[bot]@');
+        result.address = result.address?.replace('@', '[bot]@');
       }
       return result;
     }
diff --git a/lib/util/git/types.ts b/lib/util/git/types.ts
index c09dce923e..9879eee87a 100644
--- a/lib/util/git/types.ts
+++ b/lib/util/git/types.ts
@@ -3,7 +3,7 @@ import type { GitOptions } from '../../types/git';
 export type { DiffResult, StatusResult } from 'simple-git';
 
 export interface GitAuthor {
-  name?: string;
+  name?: string | null;
   address?: string;
 }
 
diff --git a/lib/util/ignore.ts b/lib/util/ignore.ts
index fc16a2e137..507e321c13 100644
--- a/lib/util/ignore.ts
+++ b/lib/util/ignore.ts
@@ -2,7 +2,7 @@ import { logger } from '../logger';
 import { regEx } from './regex';
 
 export function isSkipComment(comment?: string): boolean {
-  if (regEx(/^(renovate|pyup):/).test(comment)) {
+  if (comment && regEx(/^(renovate|pyup):/).test(comment)) {
     const command = comment.split('#')[0].split(':')[1].trim();
     if (command === 'ignore') {
       return true;
diff --git a/lib/util/index.spec.ts b/lib/util/index.spec.ts
index 5f541ec3d5..80844cd6b8 100644
--- a/lib/util/index.spec.ts
+++ b/lib/util/index.spec.ts
@@ -7,7 +7,7 @@ describe('util/index', () => {
       expect(sampleSize(array, 2)).toHaveLength(2);
     });
     it('returns full array for undefined number', () => {
-      expect(sampleSize(array, undefined)).toEqual(array);
+      expect(sampleSize(array, undefined as never)).toEqual(array);
     });
     it('returns full array for null number', () => {
       expect(sampleSize(array, null)).toBeEmptyArray();
diff --git a/lib/util/merge-confidence/index.ts b/lib/util/merge-confidence/index.ts
index ec29993be7..a36dbb620f 100644
--- a/lib/util/merge-confidence/index.ts
+++ b/lib/util/merge-confidence/index.ts
@@ -29,18 +29,19 @@ export function satisfiesConfidenceLevel(
   return confidenceLevels[confidence] >= confidenceLevels[minimumConfidence];
 }
 
-const updateTypeConfidenceMapping: Record<UpdateType, MergeConfidence> = {
-  pin: 'high',
-  digest: 'neutral',
-  bump: 'neutral',
-  lockFileMaintenance: 'neutral',
-  lockfileUpdate: 'neutral',
-  rollback: 'neutral',
-  replacement: 'neutral',
-  major: null,
-  minor: null,
-  patch: null,
-};
+const updateTypeConfidenceMapping: Record<UpdateType, MergeConfidence | null> =
+  {
+    pin: 'high',
+    digest: 'neutral',
+    bump: 'neutral',
+    lockFileMaintenance: 'neutral',
+    lockfileUpdate: 'neutral',
+    rollback: 'neutral',
+    replacement: 'neutral',
+    major: null,
+    minor: null,
+    patch: null,
+  };
 
 export async function getMergeConfidenceLevel(
   datasource: string,
diff --git a/lib/workers/branch/automerge.ts b/lib/workers/branch/automerge.ts
index c2d53a1fc5..c2d51b11b5 100644
--- a/lib/workers/branch/automerge.ts
+++ b/lib/workers/branch/automerge.ts
@@ -34,7 +34,7 @@ export async function tryBranchAutomerge(
     logger.debug(`Automerging branch`);
     try {
       if (GlobalConfig.get('dryRun')) {
-        logger.info('DRY-RUN: Would automerge branch' + config.branchName);
+        logger.info(`DRY-RUN: Would automerge branch ${config.branchName}`);
       } else {
         await mergeBranch(config.branchName);
       }
diff --git a/lib/workers/branch/index.spec.ts b/lib/workers/branch/index.spec.ts
index ca83c4e0bb..2286cb6a58 100644
--- a/lib/workers/branch/index.spec.ts
+++ b/lib/workers/branch/index.spec.ts
@@ -1,4 +1,11 @@
-import { defaultConfig, fs, git, mocked, platform } from '../../../test/util';
+import {
+  defaultConfig,
+  fs,
+  git,
+  mocked,
+  partial,
+  platform,
+} from '../../../test/util';
 import { GlobalConfig } from '../../config/global';
 import type { RepoGlobalConfig } from '../../config/types';
 import {
@@ -92,12 +99,12 @@ describe('workers/branch/index', () => {
 
       platform.massageMarkdown.mockImplementation((prBody) => prBody);
       prWorker.ensurePr.mockResolvedValue({
-        pr: {
+        pr: partial<Pr>({
           title: '',
           sourceBranch: '',
           state: '',
           body: '',
-        },
+        }),
       });
       GlobalConfig.set(adminConfig);
       sanitize.sanitize.mockImplementation((input) => input);
diff --git a/lib/workers/branch/reuse.spec.ts b/lib/workers/branch/reuse.spec.ts
index 9ffc2b0fcf..7f0f19bb20 100644
--- a/lib/workers/branch/reuse.spec.ts
+++ b/lib/workers/branch/reuse.spec.ts
@@ -9,6 +9,7 @@ jest.mock('../../util/git');
 describe('workers/branch/reuse', () => {
   describe('shouldReuseExistingBranch(config)', () => {
     const pr: Pr = {
+      number: 42,
       sourceBranch: 'master',
       state: PrState.Open,
       title: 'any',
diff --git a/lib/workers/branch/schedule.ts b/lib/workers/branch/schedule.ts
index 8dd9efc6ab..1984e57225 100644
--- a/lib/workers/branch/schedule.ts
+++ b/lib/workers/branch/schedule.ts
@@ -13,9 +13,7 @@ const scheduleMappings: Record<string, string> = {
   monthly: 'before 3am on the first day of the month',
 };
 
-export function hasValidTimezone(
-  timezone: string
-): [boolean] | [boolean, string] {
+export function hasValidTimezone(timezone: string): [true] | [false, string] {
   if (!DateTime.local().setZone(timezone).isValid) {
     return [false, `Invalid schedule: Unsupported timezone ${timezone}`];
   }
@@ -24,8 +22,8 @@ export function hasValidTimezone(
 
 export function hasValidSchedule(
   schedule: string[] | null | 'at any time'
-): [boolean] | [boolean, string] {
-  let message: string;
+): [true] | [false, string] {
+  let message = '';
   if (
     !schedule ||
     schedule === 'at any time' ||
@@ -65,7 +63,8 @@ export function hasValidSchedule(
     }
     if (
       !parsedSchedule.schedules.some(
-        (s) => s.M || s.d !== undefined || s.D || s.t_a !== undefined || s.t_b
+        (s) =>
+          !!s.M || s.d !== undefined || !!s.D || s.t_a !== undefined || !!s.t_b
       )
     ) {
       message = `Invalid schedule: "${scheduleText}" has no months, days of week or time of day`;
@@ -78,12 +77,17 @@ export function hasValidSchedule(
     // If any fail then we invalidate the whole thing
     return [false, message];
   }
-  return [true, ''];
+  return [true];
 }
 
 function cronMatches(cron: string, now: DateTime): boolean {
   const parsedCron = parseCron(cron);
 
+  // istanbul ignore if: doesn't return undefined but type will include undefined
+  if (!parsedCron) {
+    return false;
+  }
+
   if (parsedCron.hours.indexOf(now.hour) === -1) {
     // Hours mismatch
     return false;
@@ -129,9 +133,9 @@ export function isScheduledNow(config: RenovateConfig): boolean {
     );
     configSchedule = [configSchedule];
   }
-  const [validSchedule, errorMessage] = hasValidSchedule(configSchedule);
-  if (!validSchedule) {
-    logger.warn(errorMessage);
+  const validSchedule = hasValidSchedule(configSchedule);
+  if (!validSchedule[0]) {
+    logger.warn(validSchedule[1]);
     return true;
   }
   let now = DateTime.local();
@@ -139,9 +143,9 @@ export function isScheduledNow(config: RenovateConfig): boolean {
   // Adjust the time if repo is in a different timezone to renovate
   if (config.timezone) {
     logger.debug({ timezone: config.timezone }, 'Found timezone');
-    const [validTimezone, error] = hasValidTimezone(config.timezone);
-    if (!validTimezone) {
-      logger.warn(error);
+    const validTimezone = hasValidTimezone(config.timezone);
+    if (!validTimezone[0]) {
+      logger.warn(validTimezone[1]);
       return true;
     }
     logger.debug('Adjusting now for timezone');
diff --git a/lib/workers/global/config/parse/cli.spec.ts b/lib/workers/global/config/parse/cli.spec.ts
index cc9aa78092..41114a2980 100644
--- a/lib/workers/global/config/parse/cli.spec.ts
+++ b/lib/workers/global/config/parse/cli.spec.ts
@@ -1,7 +1,7 @@
-import type { RenovateOptions } from '../../../../config/types';
 import * as datasourceDocker from '../../../../datasource/docker';
 import getArgv from './__fixtures__/argv';
 import * as cli from './cli';
+import type { ParseConfigOptions } from './types';
 
 describe('workers/global/config/parse/cli', () => {
   let argv: string[];
@@ -10,13 +10,13 @@ describe('workers/global/config/parse/cli', () => {
   });
   describe('.getCliName(definition)', () => {
     it('generates CLI value', () => {
-      const option: Partial<RenovateOptions> = {
+      const option: ParseConfigOptions = {
         name: 'oneTwoThree',
       };
       expect(cli.getCliName(option)).toBe('--one-two-three');
     });
     it('generates returns empty if CLI false', () => {
-      const option: Partial<RenovateOptions> = {
+      const option: ParseConfigOptions = {
         name: 'oneTwoThree',
         cli: false,
       };
diff --git a/lib/workers/global/config/parse/cli.ts b/lib/workers/global/config/parse/cli.ts
index e62b0a80b7..6152d96181 100644
--- a/lib/workers/global/config/parse/cli.ts
+++ b/lib/workers/global/config/parse/cli.ts
@@ -1,10 +1,11 @@
 import { Command } from 'commander';
 import { getOptions } from '../../../../config/options';
-import type { AllConfig, RenovateOptions } from '../../../../config/types';
+import type { AllConfig } from '../../../../config/types';
 import { pkg } from '../../../../expose.cjs';
 import { regEx } from '../../../../util/regex';
+import type { ParseConfigOptions } from './types';
 
-export function getCliName(option: Partial<RenovateOptions>): string {
+export function getCliName(option: ParseConfigOptions): string {
   if (option.cli === false) {
     return '';
   }
diff --git a/lib/workers/global/config/parse/env.spec.ts b/lib/workers/global/config/parse/env.spec.ts
index 725685ad2f..b30a09b4e8 100644
--- a/lib/workers/global/config/parse/env.spec.ts
+++ b/lib/workers/global/config/parse/env.spec.ts
@@ -1,7 +1,7 @@
-import type { RenovateOptions } from '../../../../config/types';
 import { PlatformId } from '../../../../constants';
 import { logger } from '../../../../logger';
 import * as env from './env';
+import type { ParseConfigOptions } from './types';
 
 describe('workers/global/config/parse/env', () => {
   describe('.getConfig(env)', () => {
@@ -228,21 +228,21 @@ describe('workers/global/config/parse/env', () => {
   });
   describe('.getEnvName(definition)', () => {
     it('returns empty', () => {
-      const option: Partial<RenovateOptions> = {
+      const option: ParseConfigOptions = {
         name: 'foo',
         env: false,
       };
       expect(env.getEnvName(option)).toBe('');
     });
     it('returns existing env', () => {
-      const option: Partial<RenovateOptions> = {
+      const option: ParseConfigOptions = {
         name: 'foo',
         env: 'FOO',
       };
       expect(env.getEnvName(option)).toBe('FOO');
     });
     it('generates RENOVATE_ env', () => {
-      const option: Partial<RenovateOptions> = {
+      const option: ParseConfigOptions = {
         name: 'oneTwoThree',
       };
       expect(env.getEnvName(option)).toBe('RENOVATE_ONE_TWO_THREE');
diff --git a/lib/workers/global/config/parse/env.ts b/lib/workers/global/config/parse/env.ts
index ca96985431..212ea2f4df 100644
--- a/lib/workers/global/config/parse/env.ts
+++ b/lib/workers/global/config/parse/env.ts
@@ -1,8 +1,9 @@
 import is from '@sindresorhus/is';
 import { getOptions } from '../../../../config/options';
-import type { AllConfig, RenovateOptions } from '../../../../config/types';
+import type { AllConfig } from '../../../../config/types';
 import { PlatformId } from '../../../../constants';
 import { logger } from '../../../../logger';
+import type { ParseConfigOptions } from './types';
 
 function normalizePrefixes(
   env: NodeJS.ProcessEnv,
@@ -21,7 +22,7 @@ function normalizePrefixes(
   return result;
 }
 
-export function getEnvName(option: Partial<RenovateOptions>): string {
+export function getEnvName(option: ParseConfigOptions): string {
   if (option.env === false) {
     return '';
   }
@@ -82,27 +83,28 @@ export function getConfig(inputEnv: NodeJS.ProcessEnv): AllConfig {
   options.forEach((option) => {
     if (option.env !== false) {
       const envName = getEnvName(option);
-      if (env[envName]) {
+      const envVal = env[envName];
+      if (envVal) {
         if (option.type === 'array' && option.subType === 'object') {
           try {
-            const parsed = JSON.parse(env[envName]);
+            const parsed = JSON.parse(envVal);
             if (is.array(parsed)) {
               config[option.name] = parsed;
             } else {
               logger.debug(
-                { val: env[envName], envName },
+                { val: envVal, envName },
                 'Could not parse object array'
               );
             }
           } catch (err) {
             logger.debug(
-              { val: env[envName], envName },
+              { val: envVal, envName },
               'Could not parse environment variable'
             );
           }
         } else {
           const coerce = coersions[option.type];
-          config[option.name] = coerce(env[envName]);
+          config[option.name] = coerce(envVal);
         }
       }
     }
diff --git a/lib/workers/global/config/parse/file.ts b/lib/workers/global/config/parse/file.ts
index 66f195e0a1..4c8bdffabf 100644
--- a/lib/workers/global/config/parse/file.ts
+++ b/lib/workers/global/config/parse/file.ts
@@ -33,7 +33,7 @@ export async function getParsedContent(file: string): Promise<RenovateConfig> {
 }
 
 export async function getConfig(env: NodeJS.ProcessEnv): Promise<AllConfig> {
-  let configFile = env.RENOVATE_CONFIG_FILE || 'config.js';
+  let configFile = env.RENOVATE_CONFIG_FILE ?? 'config.js';
   if (!upath.isAbsolute(configFile)) {
     configFile = `${process.cwd()}/${configFile}`;
   }
diff --git a/lib/workers/global/config/parse/types.ts b/lib/workers/global/config/parse/types.ts
new file mode 100644
index 0000000000..53712baef5
--- /dev/null
+++ b/lib/workers/global/config/parse/types.ts
@@ -0,0 +1,4 @@
+import type { RenovateOptions } from '../../../../config/types';
+
+export type ParseConfigOptions = Partial<RenovateOptions> &
+  Pick<RenovateOptions, 'name'>;
diff --git a/lib/workers/pr/body/updates-table.ts b/lib/workers/pr/body/updates-table.ts
index bb4aebe8d8..a76abe07fd 100644
--- a/lib/workers/pr/body/updates-table.ts
+++ b/lib/workers/pr/body/updates-table.ts
@@ -5,13 +5,13 @@ import type { BranchConfig } from '../../types';
 
 type TableDefinition = {
   header: string;
-  value: string;
+  value: string | undefined;
 };
 
 function getTableDefinition(config: BranchConfig): TableDefinition[] {
   const res: TableDefinition[] = [];
-  for (const header of config.prBodyColumns) {
-    const value = config.prBodyDefinitions[header];
+  for (const header of config.prBodyColumns ?? []) {
+    const value = config.prBodyDefinitions?.[header];
     res.push({ header, value });
   }
   return res;
diff --git a/lib/workers/repository/extract/file-match.spec.ts b/lib/workers/repository/extract/file-match.spec.ts
index b23a5115bc..991dd90f51 100644
--- a/lib/workers/repository/extract/file-match.spec.ts
+++ b/lib/workers/repository/extract/file-match.spec.ts
@@ -44,7 +44,7 @@ describe('workers/repository/extract/file-match', () => {
   describe('getMatchingFiles()', () => {
     const config: RenovateConfig = {
       includePaths: [],
-      ignoredPaths: [],
+      ignorePaths: [],
       manager: 'npm',
       fileMatch: ['(^|/)package.json$'],
     };
diff --git a/lib/workers/repository/finalise/prune.spec.ts b/lib/workers/repository/finalise/prune.spec.ts
index a57909be0e..1374351bbc 100644
--- a/lib/workers/repository/finalise/prune.spec.ts
+++ b/lib/workers/repository/finalise/prune.spec.ts
@@ -81,7 +81,6 @@ describe('workers/repository/finalise/prune', () => {
     });
     it('does nothing on prune stale branches disabled', async () => {
       config.branchList = ['renovate/a', 'renovate/b'];
-      config.dryRun = false;
       config.pruneStaleBranches = false;
       git.getBranchList.mockReturnValueOnce(
         config.branchList.concat(['renovate/c'])
@@ -94,7 +93,6 @@ describe('workers/repository/finalise/prune', () => {
     });
     it('posts comment if someone pushed to PR', async () => {
       config.branchList = ['renovate/a', 'renovate/b'];
-      config.dryRun = false;
       git.getBranchList.mockReturnValueOnce(
         config.branchList.concat(['renovate/c'])
       );
@@ -136,7 +134,6 @@ describe('workers/repository/finalise/prune', () => {
     });
     it('delete branch no PR', async () => {
       config.branchList = ['renovate/a', 'renovate/b'];
-      config.dryRun = false;
       git.getBranchList.mockReturnValueOnce(
         config.branchList.concat(['renovate/c'])
       );
diff --git a/lib/workers/repository/result.ts b/lib/workers/repository/result.ts
index 9039eeab97..29faa3b545 100644
--- a/lib/workers/repository/result.ts
+++ b/lib/workers/repository/result.ts
@@ -23,8 +23,8 @@ type ProcessStatus = 'disabled' | 'enabled' | 'onboarding' | 'unknown';
 export interface ProcessResult {
   res: string;
   status: ProcessStatus;
-  enabled: boolean;
-  onboarded: boolean;
+  enabled: boolean | undefined;
+  onboarded: boolean | undefined;
 }
 
 export function processResult(
@@ -49,8 +49,8 @@ export function processResult(
   ];
   const enabledStatuses = [CONFIG_SECRETS_EXPOSED, CONFIG_VALIDATION];
   let status: ProcessStatus;
-  let enabled: boolean;
-  let onboarded: boolean;
+  let enabled: boolean | undefined;
+  let onboarded: boolean | undefined;
   // istanbul ignore next
   if (disabledStatuses.includes(res)) {
     status = 'disabled';
diff --git a/lib/workers/repository/stats.ts b/lib/workers/repository/stats.ts
index cf46588261..8a145ea8b1 100644
--- a/lib/workers/repository/stats.ts
+++ b/lib/workers/repository/stats.ts
@@ -35,6 +35,11 @@ export function printRequestStats(): void {
       `${method.toUpperCase()} ${url} ${duration} ${queueDuration}`
     );
     const { hostname } = URL.parse(url);
+
+    // istanbul ignore if: TODO: fix types (#9610)
+    if (!hostname) {
+      return;
+    }
     requestHosts[hostname] = requestHosts[hostname] || [];
     requestHosts[hostname].push(httpRequest);
   }
diff --git a/tools/docs/config.ts b/tools/docs/config.ts
index a06fa355b5..e617cf9254 100644
--- a/tools/docs/config.ts
+++ b/tools/docs/config.ts
@@ -66,14 +66,15 @@ export async function generateConfig(dist: string, bot = false): Promise<void> {
   options
     .filter((option) => option.releaseStatus !== 'unpublished')
     .forEach((option) => {
+      // TODO: fix types (#9610)
       const el: Record<string, any> = { ...option };
       let headerIndex = configOptionsRaw.indexOf(`## ${option.name}`);
       if (headerIndex === -1) {
         headerIndex = configOptionsRaw.indexOf(`### ${option.name}`);
       }
       if (bot) {
-        el.cli = getCliName(el);
-        el.env = getEnvName(el);
+        el.cli = getCliName(option);
+        el.env = getEnvName(option);
         if (el.cli === '') {
           el.cli = `N/A`;
         }
diff --git a/tsconfig.strict.json b/tsconfig.strict.json
index b94fc530cb..ae95fad7e3 100644
--- a/tsconfig.strict.json
+++ b/tsconfig.strict.json
@@ -17,31 +17,12 @@
 
     // TODO: fixme
     "**/*.spec.ts",
-    "bin/create-json-schema.js",
     "lib/config-validator.ts",
-    "lib/config/decrypt.ts",
     "lib/config/defaults.ts",
     "lib/config/index.ts",
     "lib/config/massage.ts",
     "lib/config/migrate-validate.ts",
     "lib/config/migration.ts",
-    "lib/config/migrations/base/abstract-migration.ts",
-    "lib/config/migrations/base/remove-property-migration.ts",
-    "lib/config/migrations/base/rename-property-migration.ts",
-    "lib/config/migrations/custom/binary-source-migration.ts",
-    "lib/config/migrations/custom/composer-ignore-platform-reqs-migration.ts",
-    "lib/config/migrations/custom/enabled-managers-migration.ts",
-    "lib/config/migrations/custom/go-mod-tidy-migration.ts",
-    "lib/config/migrations/custom/ignore-node-modules-migration.ts",
-    "lib/config/migrations/custom/pin-versions-migration.ts",
-    "lib/config/migrations/custom/raise-deprecation-warnings-migration.ts",
-    "lib/config/migrations/custom/rebase-conflicted-prs-migration.ts",
-    "lib/config/migrations/custom/rebase-stale-prs-migration.ts",
-    "lib/config/migrations/custom/required-status-checks-migration.ts",
-    "lib/config/migrations/custom/semantic-commits-migration.ts",
-    "lib/config/migrations/custom/trust-level-migration.ts",
-    "lib/config/migrations/custom/upgrade-in-range-migration.ts",
-    "lib/config/migrations/custom/version-strategy-migration.ts",
     "lib/config/options/index.ts",
     "lib/config/presets/azure/index.ts",
     "lib/config/presets/bitbucket-server/index.ts",
@@ -50,31 +31,12 @@
     "lib/config/presets/github/index.ts",
     "lib/config/presets/gitlab/index.ts",
     "lib/config/presets/index.ts",
-    "lib/config/presets/internal/compatibility.ts",
-    "lib/config/presets/internal/config.ts",
-    "lib/config/presets/internal/default.ts",
-    "lib/config/presets/internal/docker.ts",
-    "lib/config/presets/internal/group.ts",
-    "lib/config/presets/internal/helpers.ts",
-    "lib/config/presets/internal/index.ts",
-    "lib/config/presets/internal/monorepo.ts",
-    "lib/config/presets/internal/npm.ts",
-    "lib/config/presets/internal/packages.ts",
-    "lib/config/presets/internal/preview.ts",
-    "lib/config/presets/internal/regex-managers.ts",
-    "lib/config/presets/internal/replacements.ts",
-    "lib/config/presets/internal/schedule.ts",
-    "lib/config/presets/internal/workarounds.ts",
     "lib/config/presets/local/common.ts",
     "lib/config/presets/local/index.ts",
     "lib/config/presets/npm/index.ts",
-    "lib/config/presets/types.ts",
     "lib/config/presets/util.ts",
-    "lib/config/secrets.ts",
-    "lib/config/types.ts",
     "lib/config/utils.ts",
     "lib/config/validation-helpers/managers.ts",
-    "lib/config/validation-helpers/types.ts",
     "lib/config/validation.ts",
     "lib/datasource/adoptium-java/index.ts",
     "lib/datasource/api.ts",
@@ -344,38 +306,20 @@
     "lib/platform/azure/azure-helper.ts",
     "lib/platform/azure/index.ts",
     "lib/platform/azure/util.ts",
-    "lib/platform/bitbucket-server/index.ts",
-    "lib/platform/bitbucket-server/utils.ts",
-    "lib/platform/bitbucket/comments.ts",
     "lib/platform/bitbucket/index.ts",
-    "lib/platform/bitbucket/utils.ts",
+    "lib/platform/bitbucket-server/index.ts",
     "lib/platform/commit.ts",
-    "lib/platform/gitea/gitea-helper.ts",
     "lib/platform/gitea/index.ts",
-    "lib/platform/gitea/utils.ts",
     "lib/platform/github/index.ts",
-    "lib/platform/github/massage-markdown-links.ts",
-    "lib/platform/github/user.ts",
-    "lib/platform/gitlab/http.ts",
     "lib/platform/gitlab/index.ts",
-    "lib/platform/gitlab/merge-request.ts",
     "lib/platform/index.ts",
     "lib/renovate.ts",
     "lib/util/exec/buildpack.ts",
     "lib/util/exec/docker/index.ts",
     "lib/util/exec/index.ts",
-    "lib/util/git/auth.ts",
-    "lib/util/git/author.ts",
-    "lib/util/git/config.ts",
-    "lib/util/git/conflicts-cache.ts",
-    "lib/util/git/error.ts",
     "lib/util/git/index.ts",
     "lib/util/git/private-key.ts",
-    "lib/util/git/types.ts",
     "lib/util/git/url.ts",
-    "lib/util/host-rules.ts",
-    "lib/util/ignore.ts",
-    "lib/util/merge-confidence/index.ts",
     "lib/util/package-rules.ts",
     "lib/workers/branch/artifacts.ts",
     "lib/workers/branch/auto-replace.ts",
@@ -390,7 +334,6 @@
     "lib/workers/branch/schedule.ts",
     "lib/workers/branch/status-checks.ts",
     "lib/workers/global/autodiscover.ts",
-    "lib/workers/global/config/parse/__fixtures__/argv.ts",
     "lib/workers/global/config/parse/cli.ts",
     "lib/workers/global/config/parse/env.ts",
     "lib/workers/global/config/parse/file.ts",
@@ -398,16 +341,11 @@
     "lib/workers/global/config/parse/index.ts",
     "lib/workers/global/index.ts",
     "lib/workers/global/initialize.ts",
-    "lib/workers/global/limits.ts",
     "lib/workers/pr/automerge.ts",
     "lib/workers/pr/body/changelogs.ts",
     "lib/workers/pr/body/config-description.ts",
     "lib/workers/pr/body/controls.ts",
-    "lib/workers/pr/body/footer.ts",
-    "lib/workers/pr/body/header.ts",
     "lib/workers/pr/body/index.ts",
-    "lib/workers/pr/body/notes.ts",
-    "lib/workers/pr/body/updates-table.ts",
     "lib/workers/pr/changelog/github/index.ts",
     "lib/workers/pr/changelog/gitlab/index.ts",
     "lib/workers/pr/changelog/index.ts",
@@ -419,7 +357,6 @@
     "lib/workers/pr/index.ts",
     "lib/workers/repository/cache.ts",
     "lib/workers/repository/changelog/index.ts",
-    "lib/workers/repository/configured.ts",
     "lib/workers/repository/dependency-dashboard.ts",
     "lib/workers/repository/error-config.ts",
     "lib/workers/repository/error.ts",
@@ -463,16 +400,10 @@
     "lib/workers/repository/process/lookup/update-type.ts",
     "lib/workers/repository/process/sort.ts",
     "lib/workers/repository/process/write.ts",
-    "lib/workers/repository/result.ts",
-    "lib/workers/repository/stats.ts",
     "lib/workers/repository/updates/branch-name.ts",
     "lib/workers/repository/updates/branchify.ts",
     "lib/workers/repository/updates/flatten.ts",
     "lib/workers/repository/updates/generate.ts",
-    "lib/workers/types.ts",
-    "test/fixtures.ts",
-    "test/setup.ts",
-    "test/to-migrate.ts",
     "test/util.ts",
     "tools/docs/config.ts",
     "tools/docs/datasources.ts",
-- 
GitLab