diff --git a/lib/modules/datasource/index.ts b/lib/modules/datasource/index.ts
index 54f2aaf265f43761f77f86553821716336d1b49c..5edb609a5a9686afc54a0b651cb8532fa25968c8 100644
--- a/lib/modules/datasource/index.ts
+++ b/lib/modules/datasource/index.ts
@@ -345,7 +345,7 @@ export function getRawPkgReleases(
     return AsyncResult.err('no-package-name');
   }
 
-  return Result.wrapNullable(getRawReleases(config), 'no-result')
+  return Result.wrapNullable(getRawReleases(config), 'no-result' as const)
     .catch((e) => {
       if (e instanceof ExternalHostError) {
         e.hostType = config.datasource;
diff --git a/lib/modules/datasource/rubygems/index.ts b/lib/modules/datasource/rubygems/index.ts
index 246658368a6199360306de3ead088e87969dde4e..a2a69ec0c62e3d161de2732fa07c34698d87e9b8 100644
--- a/lib/modules/datasource/rubygems/index.ts
+++ b/lib/modules/datasource/rubygems/index.ts
@@ -13,10 +13,10 @@ import { MetadataCache } from './metadata-cache';
 import { GemInfo, MarshalledVersionInfo } from './schema';
 import { VersionsEndpointCache } from './versions-endpoint-cache';
 
-function unlessServerSide<T, E>(
-  err: E,
-  cb: () => AsyncResult<T, E>
-): AsyncResult<T, E> {
+function unlessServerSide<
+  T extends NonNullable<unknown>,
+  E extends NonNullable<unknown>
+>(err: E, cb: () => AsyncResult<T, E>): AsyncResult<T, E> {
   if (err instanceof HttpError && err.response?.statusCode) {
     const code = err.response.statusCode;
     if (code >= 500 && code <= 599) {
diff --git a/lib/modules/datasource/rubygems/metadata-cache.ts b/lib/modules/datasource/rubygems/metadata-cache.ts
index 9bae7437cf7fad75a9b8298aebf7aeb9a03aed62..41860faf0fb3a6c83574ae4b989df693f7132c31 100644
--- a/lib/modules/datasource/rubygems/metadata-cache.ts
+++ b/lib/modules/datasource/rubygems/metadata-cache.ts
@@ -23,14 +23,14 @@ export class MetadataCache {
     const cacheKey = `metadata-cache:${registryUrl}:${packageName}`;
     const hash = toSha256(versions.join(''));
 
-    const loadCache = (): AsyncResult<ReleaseResult, unknown> =>
+    const loadCache = (): AsyncResult<ReleaseResult, NonNullable<unknown>> =>
       Result.wrapNullable(
         packageCache.get<CacheRecord>(cacheNs, cacheKey),
-        'cache-not-found'
+        'cache-not-found' as const
       ).transform((cache) => {
         return hash === cache.hash
           ? Result.ok(cache.data)
-          : Result.err('cache-outdated');
+          : Result.err('cache-outdated' as const);
       });
 
     const saveCache = async (data: ReleaseResult): Promise<ReleaseResult> => {
diff --git a/lib/util/result.spec.ts b/lib/util/result.spec.ts
index 319ff8217699a16b097fa5e4a69c30cd786dff65..fe26d945238b405268dec09f6b7bc8cca30c48f9 100644
--- a/lib/util/result.spec.ts
+++ b/lib/util/result.spec.ts
@@ -40,7 +40,10 @@ describe('util/result', () => {
       });
 
       it('wraps nullable callback', () => {
-        const res = Result.wrapNullable(() => 42, 'oops');
+        const res: Result<number, 'oops'> = Result.wrapNullable(
+          (): number | null => 42,
+          'oops'
+        );
         expect(res).toEqual(Result.ok(42));
       });
 
@@ -225,7 +228,10 @@ describe('util/result', () => {
       });
 
       it('wraps nullable promise', async () => {
-        const res = Result.wrapNullable(Promise.resolve(42), 'oops');
+        const res: AsyncResult<number, 'oops'> = Result.wrapNullable(
+          Promise.resolve<number | null>(42),
+          'oops'
+        );
         await expect(res).resolves.toEqual(Result.ok(42));
       });
 
diff --git a/lib/util/result.ts b/lib/util/result.ts
index 526cf6b8898984649fa99d14883ba4690676c1e2..b41d2715d86dcafd428f9ef60bd7a1827826adf6 100644
--- a/lib/util/result.ts
+++ b/lib/util/result.ts
@@ -1,15 +1,18 @@
 import { SafeParseReturnType, ZodError } from 'zod';
 import { logger } from '../logger';
 
-interface Ok<T> {
+type Val = NonNullable<unknown>;
+type Nullable<T extends Val> = T | null | undefined;
+
+interface Ok<T extends Val> {
   readonly ok: true;
-  readonly val: NonNullable<T>;
+  readonly val: T;
   readonly err?: never;
 }
 
-interface Err<E> {
+interface Err<E extends Val> {
   readonly ok: false;
-  readonly err: NonNullable<E>;
+  readonly err: E;
   readonly val?: never;
 
   /**
@@ -19,11 +22,11 @@ interface Err<E> {
   readonly _uncaught?: true;
 }
 
-type Res<T, E> = Ok<T> | Err<E>;
+type Res<T extends Val, E extends Val> = Ok<T> | Err<E>;
 
-function isZodResult<Input, Output>(
+function isZodResult<Input, Output extends Val>(
   input: unknown
-): input is SafeParseReturnType<Input, NonNullable<Output>> {
+): input is SafeParseReturnType<Input, Output> {
   if (
     typeof input !== 'object' ||
     input === null ||
@@ -45,9 +48,9 @@ function isZodResult<Input, Output>(
   }
 }
 
-function fromZodResult<Input, Output>(
-  input: SafeParseReturnType<Input, NonNullable<Output>>
-): Result<Output, ZodError<Input>> {
+function fromZodResult<ZodInput, ZodOutput extends Val>(
+  input: SafeParseReturnType<ZodInput, ZodOutput>
+): Result<ZodOutput, ZodError<ZodInput>> {
   return input.success ? Result.ok(input.data) : Result.err(input.error);
 }
 
@@ -55,9 +58,9 @@ function fromZodResult<Input, Output>(
  * All non-nullable values that also are not Promises nor Zod results.
  * It's useful for restricting Zod results to not return `null` or `undefined`.
  */
-type RawValue<T> = Exclude<
-  NonNullable<T>,
-  SafeParseReturnType<unknown, NonNullable<T>> | Promise<unknown>
+type RawValue<T extends Val> = Exclude<
+  T,
+  SafeParseReturnType<unknown, T> | Promise<unknown>
 >;
 
 /**
@@ -68,18 +71,18 @@ type RawValue<T> = Exclude<
  * - `.transform()` are pipes which can be chained
  * - `.unwrap()` is the point of consumption
  */
-export class Result<T, E = Error> {
+export class Result<T extends Val, E extends Val = Error> {
   private constructor(private readonly res: Res<T, E>) {}
 
-  static ok<T>(val: NonNullable<T>): Result<T, never> {
+  static ok<T extends Val>(val: T): Result<T, never> {
     return new Result({ ok: true, val });
   }
 
-  static err<E>(err: NonNullable<E>): Result<never, E> {
+  static err<E extends Val>(err: E): Result<never, E> {
     return new Result({ ok: false, err });
   }
 
-  static _uncaught<E>(err: NonNullable<E>): Result<never, E> {
+  static _uncaught<E extends Val>(err: E): Result<never, E> {
     return new Result({ ok: false, err, _uncaught: true });
   }
 
@@ -112,17 +115,26 @@ export class Result<T, E = Error> {
    *
    *   ```
    */
-  static wrap<T, Input = any>(
-    zodResult: SafeParseReturnType<Input, NonNullable<T>>
+  static wrap<T extends Val, Input = any>(
+    zodResult: SafeParseReturnType<Input, T>
   ): Result<T, ZodError<Input>>;
-  static wrap<T, E = Error>(callback: () => RawValue<T>): Result<T, E>;
-  static wrap<T, E = Error, EE = never>(
+  static wrap<T extends Val, E extends Val = Error>(
+    callback: () => RawValue<T>
+  ): Result<T, E>;
+  static wrap<T extends Val, E extends Val = Error, EE extends Val = never>(
     promise: Promise<Result<T, EE>>
   ): AsyncResult<T, E | EE>;
-  static wrap<T, E = Error>(promise: Promise<RawValue<T>>): AsyncResult<T, E>;
-  static wrap<T, E = Error, EE = never, Input = any>(
+  static wrap<T extends Val, E extends Val = Error>(
+    promise: Promise<RawValue<T>>
+  ): AsyncResult<T, E>;
+  static wrap<
+    T extends Val,
+    E extends Val = Error,
+    EE extends Val = never,
+    Input = any
+  >(
     input:
-      | SafeParseReturnType<Input, NonNullable<T>>
+      | SafeParseReturnType<Input, T>
       | (() => RawValue<T>)
       | Promise<Result<T, EE>>
       | Promise<RawValue<T>>
@@ -151,7 +163,7 @@ export class Result<T, E = Error> {
    * hence never re-thrown.
    *
    * Since functions and promises returning nullable can't be wrapped with `Result.wrap()`
-   * because `val` is constrained by being `NonNullable<T>`, `null` and `undefined`
+   * because `val` is constrained by being `NonNullable`, `null` and `undefined`
    * must be converted to some sort of `err` value.
    *
    * This method does exactly this, i.g. it is the feature-rich shorthand for:
@@ -187,47 +199,70 @@ export class Result<T, E = Error> {
    *
    *   ```
    */
-  static wrapNullable<T, E = Error, NullableError = Error>(
-    callback: () => T,
-    nullableError: NonNullable<NullableError>
-  ): Result<T, E | NullableError>;
-  static wrapNullable<T, E = Error, NullError = Error, UndefinedError = Error>(
-    callback: () => T,
-    nullError: NonNullable<NullError>,
-    undefinedError: NonNullable<UndefinedError>
-  ): Result<T, E | NullError | UndefinedError>;
-  static wrapNullable<T, E = Error, NullableError = Error>(
-    promise: Promise<T>,
-    nullableError: NonNullable<NullableError>
-  ): AsyncResult<T, E | NullableError>;
-  static wrapNullable<T, E = Error, NullError = Error, UndefinedError = Error>(
-    promise: Promise<T>,
-    nullError: NonNullable<NullError>,
-    undefinedError: NonNullable<UndefinedError>
-  ): AsyncResult<T, E | NullError | UndefinedError>;
-  static wrapNullable<T, E = Error, NullError = Error, UndefinedError = Error>(
-    input: (() => T) | Promise<T>,
-    arg2: NonNullable<NullError>,
-    arg3?: NonNullable<UndefinedError>
+  static wrapNullable<
+    T extends Val,
+    E extends Val = Error,
+    ErrForNullable extends Val = Error
+  >(
+    callback: () => Nullable<T>,
+    errForNullable: ErrForNullable
+  ): Result<T, E | ErrForNullable>;
+  static wrapNullable<
+    T extends Val,
+    E extends Val = Error,
+    ErrForNull extends Val = Error,
+    ErrForUndefined extends Val = Error
+  >(
+    callback: () => Nullable<T>,
+    errForNull: ErrForNull,
+    errForUndefined: ErrForUndefined
+  ): Result<T, E | ErrForNull | ErrForUndefined>;
+  static wrapNullable<
+    T extends Val,
+    E extends Val = Error,
+    ErrForNullable extends Val = Error
+  >(
+    promise: Promise<Nullable<T>>,
+    errForNullable: ErrForNullable
+  ): AsyncResult<T, E | ErrForNullable>;
+  static wrapNullable<
+    T extends Val,
+    E extends Val = Error,
+    ErrForNull extends Val = Error,
+    ErrForUndefined extends Val = Error
+  >(
+    promise: Promise<Nullable<T>>,
+    errForNull: ErrForNull,
+    errForUndefined: ErrForUndefined
+  ): AsyncResult<T, E | ErrForNull | ErrForUndefined>;
+  static wrapNullable<
+    T extends Val,
+    E extends Val = Error,
+    ErrForNull extends Val = Error,
+    ErrForUndefined extends Val = Error
+  >(
+    input: (() => Nullable<T>) | Promise<Nullable<T>>,
+    arg2: ErrForNull,
+    arg3?: ErrForUndefined
   ):
-    | Result<T, E | NullError | UndefinedError>
-    | AsyncResult<T, E | NullError | UndefinedError> {
-    const nullError = arg2;
-    const undefinedError = arg3 ?? arg2;
+    | Result<T, E | ErrForNull | ErrForUndefined>
+    | AsyncResult<T, E | ErrForNull | ErrForUndefined> {
+    const errForNull = arg2;
+    const errForUndefined = arg3 ?? arg2;
 
     if (input instanceof Promise) {
-      return AsyncResult.wrapNullable(input, nullError, undefinedError);
+      return AsyncResult.wrapNullable(input, errForNull, errForUndefined);
     }
 
     try {
       const result = input();
 
       if (result === null) {
-        return Result.err(nullError);
+        return Result.err(errForNull);
       }
 
       if (result === undefined) {
-        return Result.err(undefinedError);
+        return Result.err(errForUndefined);
       }
 
       return Result.ok(result);
@@ -255,8 +290,8 @@ export class Result<T, E = Error> {
    *   ```
    */
   unwrap(): Res<T, E>;
-  unwrap(fallback: NonNullable<T>): NonNullable<T>;
-  unwrap(fallback?: NonNullable<T>): Res<T, E> | NonNullable<T> {
+  unwrap(fallback: T): T;
+  unwrap(fallback?: T): Res<T, E> | T {
     if (this.res.ok) {
       return fallback === undefined ? this.res : this.res.val;
     }
@@ -275,7 +310,7 @@ export class Result<T, E = Error> {
   /**
    * Returns the ok-value or throw the error.
    */
-  unwrapOrThrow(): NonNullable<T> {
+  unwrapOrThrow(): T {
     if (this.res.ok) {
       return this.res.val;
     }
@@ -309,30 +344,28 @@ export class Result<T, E = Error> {
    *
    *   ```
    */
-  transform<U, EE>(
-    fn: (value: NonNullable<T>) => Result<U, E | EE>
+  transform<U extends Val, EE extends Val>(
+    fn: (value: T) => Result<U, E | EE>
   ): Result<U, E | EE>;
-  transform<U, EE>(
-    fn: (value: NonNullable<T>) => AsyncResult<U, E | EE>
+  transform<U extends Val, EE extends Val>(
+    fn: (value: T) => AsyncResult<U, E | EE>
   ): AsyncResult<U, E | EE>;
-  transform<U, Input = any>(
-    fn: (value: NonNullable<T>) => SafeParseReturnType<Input, NonNullable<U>>
+  transform<U extends Val, Input = any>(
+    fn: (value: T) => SafeParseReturnType<Input, NonNullable<U>>
   ): Result<U, E | ZodError<Input>>;
-  transform<U, Input = any>(
-    fn: (
-      value: NonNullable<T>
-    ) => Promise<SafeParseReturnType<Input, NonNullable<U>>>
+  transform<U extends Val, Input = any>(
+    fn: (value: T) => Promise<SafeParseReturnType<Input, NonNullable<U>>>
   ): AsyncResult<U, E | ZodError<Input>>;
-  transform<U, EE>(
-    fn: (value: NonNullable<T>) => Promise<Result<U, E | EE>>
+  transform<U extends Val, EE extends Val>(
+    fn: (value: T) => Promise<Result<U, E | EE>>
   ): AsyncResult<U, E | EE>;
-  transform<U>(
-    fn: (value: NonNullable<T>) => Promise<RawValue<U>>
+  transform<U extends Val>(
+    fn: (value: T) => Promise<RawValue<U>>
   ): AsyncResult<U, E>;
-  transform<U>(fn: (value: NonNullable<T>) => RawValue<U>): Result<U, E>;
-  transform<U, EE, Input = any>(
+  transform<U extends Val>(fn: (value: T) => RawValue<U>): Result<U, E>;
+  transform<U extends Val, EE extends Val, Input = any>(
     fn: (
-      value: NonNullable<T>
+      value: T
     ) =>
       | Result<U, E | EE>
       | AsyncResult<U, E | EE>
@@ -377,18 +410,18 @@ export class Result<T, E = Error> {
     }
   }
 
-  catch<U = T, EE = E>(
-    fn: (err: NonNullable<E>) => Result<U, E | EE>
+  catch<U extends Val = T, EE extends Val = E>(
+    fn: (err: E) => Result<U, E | EE>
   ): Result<T | U, E | EE>;
-  catch<U = T, EE = E>(
-    fn: (err: NonNullable<E>) => AsyncResult<U, E | EE>
+  catch<U extends Val = T, EE extends Val = E>(
+    fn: (err: E) => AsyncResult<U, E | EE>
   ): AsyncResult<T | U, E | EE>;
-  catch<U = T, EE = E>(
-    fn: (err: NonNullable<E>) => Promise<Result<U, E | EE>>
+  catch<U extends Val = T, EE extends Val = E>(
+    fn: (err: E) => Promise<Result<U, E | EE>>
   ): AsyncResult<T | U, E | EE>;
-  catch<U = T, EE = E>(
+  catch<U extends Val = T, EE extends Val = E>(
     fn: (
-      err: NonNullable<E>
+      err: E
     ) => Result<U, E | EE> | AsyncResult<U, E | EE> | Promise<Result<U, E | EE>>
   ): Result<T | U, E | EE> | AsyncResult<T | U, E | EE> {
     if (this.res.ok) {
@@ -426,7 +459,9 @@ export class Result<T, E = Error> {
  *
  * All the methods resemble `Result` methods, but work asynchronously.
  */
-export class AsyncResult<T, E> implements PromiseLike<Result<T, E>> {
+export class AsyncResult<T extends Val, E extends Val>
+  implements PromiseLike<Result<T, E>>
+{
   private constructor(private asyncResult: Promise<Result<T, E>>) {}
 
   then<TResult1 = Result<T, E>>(
@@ -438,18 +473,23 @@ export class AsyncResult<T, E> implements PromiseLike<Result<T, E>> {
     return this.asyncResult.then(onfulfilled);
   }
 
-  static ok<T>(val: NonNullable<T>): AsyncResult<T, never> {
+  static ok<T extends Val>(val: T): AsyncResult<T, never> {
     return new AsyncResult(Promise.resolve(Result.ok(val)));
   }
 
-  static err<E>(err: NonNullable<E>): AsyncResult<never, E> {
+  static err<E extends Val>(err: NonNullable<E>): AsyncResult<never, E> {
     // eslint-disable-next-line promise/no-promise-in-callback
     return new AsyncResult(Promise.resolve(Result.err(err)));
   }
 
-  static wrap<T, E = Error, EE = never, Input = any>(
+  static wrap<
+    T extends Val,
+    E extends Val = Error,
+    EE extends Val = never,
+    Input = any
+  >(
     promise:
-      | Promise<SafeParseReturnType<Input, NonNullable<T>>>
+      | Promise<SafeParseReturnType<Input, T>>
       | Promise<Result<T, EE>>
       | Promise<RawValue<T>>,
     onErr?: (err: NonNullable<E>) => Result<T, E>
@@ -476,20 +516,25 @@ export class AsyncResult<T, E> implements PromiseLike<Result<T, E>> {
     );
   }
 
-  static wrapNullable<T, E, NullError, UndefinedError>(
-    promise: Promise<T>,
-    nullError: NonNullable<NullError>,
-    undefinedError: NonNullable<UndefinedError>
-  ): AsyncResult<T, E | NullError | UndefinedError> {
+  static wrapNullable<
+    T extends Val,
+    E extends Val,
+    ErrForNull extends Val,
+    ErrForUndefined extends Val
+  >(
+    promise: Promise<Nullable<T>>,
+    errForNull: NonNullable<ErrForNull>,
+    errForUndefined: NonNullable<ErrForUndefined>
+  ): AsyncResult<T, E | ErrForNull | ErrForUndefined> {
     return new AsyncResult(
       promise
         .then((value) => {
           if (value === null) {
-            return Result.err(nullError);
+            return Result.err(errForNull);
           }
 
           if (value === undefined) {
-            return Result.err(undefinedError);
+            return Result.err(errForUndefined);
           }
 
           return Result.ok(value);
@@ -517,19 +562,17 @@ export class AsyncResult<T, E> implements PromiseLike<Result<T, E>> {
    *   ```
    */
   unwrap(): Promise<Res<T, E>>;
-  unwrap(fallback: NonNullable<T>): Promise<NonNullable<T>>;
-  unwrap(
-    fallback?: NonNullable<T>
-  ): Promise<Res<T, E>> | Promise<NonNullable<T>> {
+  unwrap(fallback: T): Promise<T>;
+  unwrap(fallback?: T): Promise<Res<T, E>> | Promise<T> {
     return fallback === undefined
       ? this.asyncResult.then<Res<T, E>>((res) => res.unwrap())
-      : this.asyncResult.then<NonNullable<T>>((res) => res.unwrap(fallback));
+      : this.asyncResult.then<T>((res) => res.unwrap(fallback));
   }
 
   /**
    * Returns the ok-value or throw the error.
    */
-  async unwrapOrThrow(): Promise<NonNullable<T>> {
+  async unwrapOrThrow(): Promise<T> {
     const result = await this.asyncResult;
     return result.unwrapOrThrow();
   }
@@ -553,30 +596,28 @@ export class AsyncResult<T, E> implements PromiseLike<Result<T, E>> {
    *
    *   ```
    */
-  transform<U, EE>(
-    fn: (value: NonNullable<T>) => Result<U, E | EE>
+  transform<U extends Val, EE extends Val>(
+    fn: (value: T) => Result<U, E | EE>
   ): AsyncResult<U, E | EE>;
-  transform<U, EE>(
-    fn: (value: NonNullable<T>) => AsyncResult<U, E | EE>
+  transform<U extends Val, EE extends Val>(
+    fn: (value: T) => AsyncResult<U, E | EE>
   ): AsyncResult<U, E | EE>;
-  transform<U, Input = any>(
-    fn: (value: NonNullable<T>) => SafeParseReturnType<Input, NonNullable<U>>
+  transform<U extends Val, Input = any>(
+    fn: (value: T) => SafeParseReturnType<Input, NonNullable<U>>
   ): AsyncResult<U, E | ZodError<Input>>;
-  transform<U, Input = any>(
-    fn: (
-      value: NonNullable<T>
-    ) => Promise<SafeParseReturnType<Input, NonNullable<U>>>
+  transform<U extends Val, Input = any>(
+    fn: (value: T) => Promise<SafeParseReturnType<Input, NonNullable<U>>>
   ): AsyncResult<U, E | ZodError<Input>>;
-  transform<U, EE>(
-    fn: (value: NonNullable<T>) => Promise<Result<U, E | EE>>
+  transform<U extends Val, EE extends Val>(
+    fn: (value: T) => Promise<Result<U, E | EE>>
   ): AsyncResult<U, E | EE>;
-  transform<U>(
-    fn: (value: NonNullable<T>) => Promise<RawValue<U>>
+  transform<U extends Val>(
+    fn: (value: T) => Promise<RawValue<U>>
   ): AsyncResult<U, E>;
-  transform<U>(fn: (value: NonNullable<T>) => RawValue<U>): AsyncResult<U, E>;
-  transform<U, EE, Input = any>(
+  transform<U extends Val>(fn: (value: T) => RawValue<U>): AsyncResult<U, E>;
+  transform<U extends Val, EE extends Val, Input = any>(
     fn: (
-      value: NonNullable<T>
+      value: T
     ) =>
       | Result<U, E | EE>
       | AsyncResult<U, E | EE>
@@ -632,16 +673,16 @@ export class AsyncResult<T, E> implements PromiseLike<Result<T, E>> {
     );
   }
 
-  catch<U = T, EE = E>(
+  catch<U extends Val = T, EE extends Val = E>(
     fn: (err: NonNullable<E>) => Result<U, E | EE>
   ): AsyncResult<T | U, E | EE>;
-  catch<U = T, EE = E>(
+  catch<U extends Val = T, EE extends Val = E>(
     fn: (err: NonNullable<E>) => AsyncResult<U, E | EE>
   ): AsyncResult<T | U, E | EE>;
-  catch<U = T, EE = E>(
+  catch<U extends Val = T, EE extends Val = E>(
     fn: (err: NonNullable<E>) => Promise<Result<U, E | EE>>
   ): AsyncResult<T | U, E | EE>;
-  catch<U = T, EE = E>(
+  catch<U extends Val = T, EE extends Val = E>(
     fn: (
       err: NonNullable<E>
     ) => Result<U, E | EE> | AsyncResult<U, E | EE> | Promise<Result<U, E | EE>>