diff --git a/typo3/sysext/core/Classes/Utility/StringUtility.php b/typo3/sysext/core/Classes/Utility/StringUtility.php
index f8ec246d14408bb10a762ec2b35502386c7c393c..2b69abb8f38a9c8ebcd744d59dd0fb2e06295365 100644
--- a/typo3/sysext/core/Classes/Utility/StringUtility.php
+++ b/typo3/sysext/core/Classes/Utility/StringUtility.php
@@ -20,6 +20,38 @@ namespace TYPO3\CMS\Core\Utility;
  */
 class StringUtility
 {
+    /**
+     * Casts applicable types (string, bool, finite numeric) to string.
+     *
+     * Any other type will be replaced by the `$default` value.
+     *
+     * @param mixed $value
+     */
+    public static function cast($value, ?string $default = null): ?string
+    {
+        if (is_string($value)) {
+            return $value;
+        }
+
+        if (is_bool($value) || (is_numeric($value) && is_finite($value))) {
+            return (string)$value;
+        }
+
+        return $default;
+    }
+
+    /**
+     * Keeps only string types (filters out non-strings).
+     *
+     * Any other non-string type will be replaced by the `$default` value.
+     *
+     * @param mixed $value
+     */
+    public static function filter($value, ?string $default = null): ?string
+    {
+        return is_string($value) ? $value : $default;
+    }
+
     /**
      * Returns TRUE if $haystack begins with $needle.
      * The input string is not trimmed before and search is done case sensitive.
diff --git a/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php b/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php
index b8a464245310649e0307b013aa7af923130b6ffb..381f439b5582022082d1677ce38771cf144a12a5 100644
--- a/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php
+++ b/typo3/sysext/core/Tests/Unit/Utility/StringUtilityTest.php
@@ -25,6 +25,129 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
  */
 class StringUtilityTest extends UnitTestCase
 {
+    /**
+     * @return \Generator<string, array{0: mixed}>
+     */
+    public static function stringCastableValuesDataProvider(): \Generator
+    {
+        yield 'empty string' => [''];
+        yield 'string' => ['value'];
+        yield 'int' => [1];
+        yield 'float' => [1.2345];
+        yield 'bool' => [true];
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @test
+     * @dataProvider stringCastableValuesDataProvider
+     */
+    public function castWithStringCastableReturnsValueCastToString($value): void
+    {
+        $expected = (string)$value;
+
+        self::assertSame($expected, StringUtility::cast($value, 'default'));
+    }
+
+    /**
+     * @return \Generator<string, array{0: mixed}>
+     */
+    public static function nonStringCastableValuesDataProvider(): \Generator
+    {
+        yield 'array' => [['1']];
+        yield 'null' => [null];
+        yield 'object' => [new \stdClass()];
+        yield 'closure' => [static fn (): string => 'fn'];
+        // PHP interprets it as `lim(x→0) log(x) = -∞`
+        yield 'infinite' => [log(0)];
+        // acos only supports values in range [-1; +1]
+        yield 'NaN' => [acos(2)];
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @test
+     * @dataProvider nonStringCastableValuesDataProvider
+     */
+    public function castWithWithNonStringCastableReturnsDefault($value): void
+    {
+        $default = 'default';
+
+        self::assertSame($default, StringUtility::cast($value, $default));
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @test
+     * @dataProvider nonStringCastableValuesDataProvider
+     */
+    public function castWithWithNonStringCastableAndNoDefaultProvidedReturnsNull($value): void
+    {
+        self::assertNull(StringUtility::cast($value));
+    }
+
+    /**
+     * @return \Generator<string, array{0: mixed}>
+     */
+    public static function nonStringValueToFilterDataProvider(): \Generator
+    {
+        yield 'int' => [1];
+        yield 'float' => [1.2345];
+        yield 'bool' => [true];
+        yield 'array' => [['1']];
+        yield 'null' => [null];
+        yield 'object' => [new \stdClass()];
+        yield 'closure' => [static fn (): string => 'fn'];
+        // PHP interprets it as `lim(x→0) log(x) = -∞`
+        yield 'infinite' => [log(0)];
+        // acos only supports values in range [-1; +1]
+        yield 'NaN' => [acos(2)];
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @test
+     * @dataProvider nonStringValueToFilterDataProvider
+     */
+    public function filterForNonStringValueAndDefaultProvidedReturnsDefault($value): void
+    {
+        $default = 'default';
+
+        self::assertSame($default, StringUtility::filter($value, $default));
+    }
+
+    /**
+     * @param mixed $value
+     *
+     * @test
+     * @dataProvider nonStringValueToFilterDataProvider
+     */
+    public function filterForNonStringValueAndNoDefaultProvidedReturnsNull($value): void
+    {
+        self::assertNull(StringUtility::filter($value));
+    }
+
+    /**
+     * @return \Generator<string, array{0: string}>
+     */
+    public static function stringValueToFilterDataProvider(): \Generator
+    {
+        yield 'empty string' => [''];
+        yield 'non-empty string' => ['value'];
+    }
+    /**
+     * @test
+     * @dataProvider stringValueToFilterDataProvider
+     */
+    public function filterForStringValuesReturnsProvidedValue(string $value): void
+    {
+        self::assertSame($value, StringUtility::filter($value, 'some default'));
+    }
+
     /**
      * @test
      */