diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/Link/TypolinkViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/Link/TypolinkViewHelper.php
index 303d61e28edf36d185937f5da7ca1057d58c7b45..b0bd716d9889671b77b5e5c47b9491a99a45dae7 100644
--- a/typo3/sysext/fluid/Classes/ViewHelpers/Link/TypolinkViewHelper.php
+++ b/typo3/sysext/fluid/Classes/ViewHelpers/Link/TypolinkViewHelper.php
@@ -20,6 +20,7 @@ namespace TYPO3\CMS\Fluid\ViewHelpers\Link;
 use TYPO3\CMS\Core\LinkHandling\TypoLinkCodecService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Typolink\TypolinkParameter;
 use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
 use TYPO3Fluid\Fluid\Core\Variables\ScopedVariableProvider;
 use TYPO3Fluid\Fluid\Core\Variables\StandardVariableProvider;
@@ -101,7 +102,7 @@ final class TypolinkViewHelper extends AbstractViewHelper
 
     public function initializeArguments(): void
     {
-        $this->registerArgument('parameter', 'string', 'stdWrap.typolink style parameter string', true);
+        $this->registerArgument('parameter', 'mixed', 'stdWrap.typolink style parameter string', true);
         $this->registerArgument('target', 'string', 'Define where to display the linked URL', false, '');
         $this->registerArgument('class', 'string', 'Define classes for the link element', false, '');
         $this->registerArgument('title', 'string', 'Define the title for the link element', false, '');
@@ -123,23 +124,28 @@ final class TypolinkViewHelper extends AbstractViewHelper
     {
         $parameter = $arguments['parameter'] ?? '';
         $partsAs = $arguments['parts-as'] ?? 'typoLinkParts';
+        $typoLinkCodecService = GeneralUtility::makeInstance(TypoLinkCodecService::class);
+
+        if (!$parameter instanceof TypolinkParameter) {
+            $parameter = TypolinkParameter::createFromTypolinkParts(
+                is_scalar($parameter) ? $typoLinkCodecService->decode((string)$parameter) : []
+            );
+        }
 
-        $typoLinkCodec = GeneralUtility::makeInstance(TypoLinkCodecService::class);
-        $typoLinkConfiguration = $typoLinkCodec->decode((string)$parameter);
         // Merge the $parameter with other arguments
-        $mergedTypoLinkConfiguration = self::mergeTypoLinkConfiguration($typoLinkConfiguration, $arguments);
-        $typoLinkParameter = $typoLinkCodec->encode($mergedTypoLinkConfiguration);
+        $typolinkParameter = TypolinkParameter::createFromTypolinkParts(self::mergeTypoLinkConfiguration($parameter->toArray(), $arguments))->toArray();
 
         // expose internal typoLink configuration to Fluid child context
-        $variableProvider = new ScopedVariableProvider($renderingContext->getVariableProvider(), new StandardVariableProvider([$partsAs => $typoLinkConfiguration]));
+        $variableProvider = new ScopedVariableProvider($renderingContext->getVariableProvider(), new StandardVariableProvider([$partsAs => $typolinkParameter]));
         $renderingContext->setVariableProvider($variableProvider);
         // If no link has to be rendered, the inner content will be returned as such
         $content = (string)$renderChildrenClosure();
         // clean up exposed variables
         $renderingContext->setVariableProvider($variableProvider->getGlobalVariableProvider());
 
-        if ($parameter) {
-            $content = self::invokeContentObjectRenderer($arguments, $typoLinkParameter, $content);
+        $typolink = $typoLinkCodecService->encode($typolinkParameter);
+        if ($typolink !== '') {
+            $content = self::invokeContentObjectRenderer($arguments, $typolink, $content);
         }
         return $content;
     }
diff --git a/typo3/sysext/fluid/Classes/ViewHelpers/Uri/TypolinkViewHelper.php b/typo3/sysext/fluid/Classes/ViewHelpers/Uri/TypolinkViewHelper.php
index 7a346a1995d7d7700f24cc936536a60aff05841f..a6f47669abeb212cc0399a74d8f9c0343abfbcad 100644
--- a/typo3/sysext/fluid/Classes/ViewHelpers/Uri/TypolinkViewHelper.php
+++ b/typo3/sysext/fluid/Classes/ViewHelpers/Uri/TypolinkViewHelper.php
@@ -20,6 +20,7 @@ namespace TYPO3\CMS\Fluid\ViewHelpers\Uri;
 use TYPO3\CMS\Core\LinkHandling\TypoLinkCodecService;
 use TYPO3\CMS\Core\Utility\GeneralUtility;
 use TYPO3\CMS\Frontend\ContentObject\ContentObjectRenderer;
+use TYPO3\CMS\Frontend\Typolink\TypolinkParameter;
 use TYPO3Fluid\Fluid\Core\Rendering\RenderingContextInterface;
 use TYPO3Fluid\Fluid\Core\ViewHelper\AbstractViewHelper;
 use TYPO3Fluid\Fluid\Core\ViewHelper\Traits\CompileWithRenderStatic;
@@ -64,7 +65,7 @@ final class TypolinkViewHelper extends AbstractViewHelper
 
     public function initializeArguments(): void
     {
-        $this->registerArgument('parameter', 'string', 'stdWrap.typolink style parameter string', true);
+        $this->registerArgument('parameter', 'mixed', 'stdWrap.typolink style parameter string', true);
         $this->registerArgument('additionalParams', 'string', 'stdWrap.typolink additionalParams', false, '');
         $this->registerArgument('language', 'string', 'link to a specific language - defaults to the current language, use a language ID or "current" to enforce a specific language');
         $this->registerArgument('addQueryString', 'string', 'If set, the current query parameters will be kept in the URL. If set to "untrusted", then ALL query parameters will be added. Be aware, that this might lead to problems when the generated link is cached.', false, false);
@@ -75,17 +76,19 @@ final class TypolinkViewHelper extends AbstractViewHelper
     public static function renderStatic(array $arguments, \Closure $renderChildrenClosure, RenderingContextInterface $renderingContext): string
     {
         $parameter = $arguments['parameter'] ?? '';
+        $typoLinkCodecService = GeneralUtility::makeInstance(TypoLinkCodecService::class);
 
-        $typoLinkCodec = GeneralUtility::makeInstance(TypoLinkCodecService::class);
-        $typoLinkConfiguration = $typoLinkCodec->decode((string)$parameter);
-        $mergedTypoLinkConfiguration = self::mergeTypoLinkConfiguration($typoLinkConfiguration, $arguments);
-        $typoLinkParameter = $typoLinkCodec->encode($mergedTypoLinkConfiguration);
-
-        $content = '';
-        if ($parameter) {
-            $content = self::invokeContentObjectRenderer($arguments, $typoLinkParameter);
+        if (!$parameter instanceof TypolinkParameter) {
+            $parameter = TypolinkParameter::createFromTypolinkParts(
+                is_scalar($parameter) ? $typoLinkCodecService->decode((string)$parameter) : []
+            );
         }
-        return $content;
+
+        // Merge the $parameter with other arguments and encode the typolink again
+        $typolink = $typoLinkCodecService->encode(
+            TypolinkParameter::createFromTypolinkParts(self::mergeTypoLinkConfiguration($parameter->toArray(), $arguments))->toArray()
+        );
+        return $typolink !== '' ? self::invokeContentObjectRenderer($arguments, $typolink) : '';
     }
 
     protected static function invokeContentObjectRenderer(array $arguments, string $typoLinkParameter): string
diff --git a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Link/TypolinkViewHelperTest.php b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Link/TypolinkViewHelperTest.php
index 8f7a54239b3b7bee30da283ea9ca6a19fbbb45d3..c9316f5896f1184c3948b7dd76ef166950e7ad68 100644
--- a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Link/TypolinkViewHelperTest.php
+++ b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Link/TypolinkViewHelperTest.php
@@ -26,6 +26,7 @@ use TYPO3\CMS\Core\Routing\PageArguments;
 use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait;
 use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextFactory;
+use TYPO3\CMS\Frontend\Typolink\TypolinkParameter;
 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
 use TYPO3Fluid\Fluid\View\TemplateView;
@@ -69,6 +70,10 @@ final class TypolinkViewHelperTest extends FunctionalTestCase
     public static function renderDataProvider(): array
     {
         return [
+            'empty link' => [
+                '<f:link.typolink parameter="">This is a testlink</f:link.typolink>',
+                'This is a testlink',
+            ],
             'link: default' => [
                 '<f:link.typolink parameter="1">This is a testlink</f:link.typolink>',
                 '<a href="/en/">This is a testlink</a>',
@@ -177,6 +182,13 @@ EOT
     public static function renderWithAssignedParametersDataProvider(): array
     {
         return [
+            'empty parameter' => [
+                '<f:link.typolink parameter="{parameter}">Link text</f:link.typolink>',
+                [
+                    'parameter' => '',
+                ],
+                'Link text',
+            ],
             'target _self' => [
                 '<f:link.typolink parameter="{parameter}" parts-as="typoLinkParts">Individual {typoLinkParts.target} {typoLinkParts.class} {typoLinkParts.title}</f:link.typolink>',
                 [
@@ -213,6 +225,20 @@ EOT
                 ],
                 '<a href="http://typo3.org/" target="_self">_self</a>',
             ],
+            'typolinkParameter object' => [
+                '<f:link.typolink parameter="{parameter}" parts-as="typoLinkParts">{typoLinkParts.target}</f:link.typolink>{typoLinkParts.target}',
+                [
+                    'parameter' => new TypolinkParameter('http://typo3.org/', '_self'),
+                ],
+                '<a href="http://typo3.org/" target="_self">_self</a>',
+            ],
+            'invalid parameter' => [
+                '<f:link.typolink parameter="{parameter}" parts-as="typoLinkParts">{typoLinkParts.target}</f:link.typolink>{typoLinkParts.target}',
+                [
+                    'parameter' => new \stdClass(),
+                ],
+                '',
+            ],
         ];
     }
 
diff --git a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/TypolinkViewHelperTest.php b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/TypolinkViewHelperTest.php
index 57c3eeb3b8b9a1084e3521393a2a49f5caf5e73e..a8eaf223a0ee3279c6029456ca009ce18819af45 100644
--- a/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/TypolinkViewHelperTest.php
+++ b/typo3/sysext/fluid/Tests/Functional/ViewHelpers/Uri/TypolinkViewHelperTest.php
@@ -19,10 +19,17 @@ namespace TYPO3\CMS\Fluid\Tests\Functional\ViewHelpers\Uri;
 
 use PHPUnit\Framework\Attributes\DataProvider;
 use PHPUnit\Framework\Attributes\Test;
+use TYPO3\CMS\Core\Core\SystemEnvironmentBuilder;
 use TYPO3\CMS\Core\Database\ConnectionPool;
+use TYPO3\CMS\Core\Http\ServerRequest;
+use TYPO3\CMS\Core\Routing\PageArguments;
+use TYPO3\CMS\Core\Site\Entity\Site;
 use TYPO3\CMS\Core\Tests\Functional\SiteHandling\SiteBasedTestTrait;
+use TYPO3\CMS\Fluid\Core\Rendering\RenderingContextFactory;
+use TYPO3\CMS\Frontend\Typolink\TypolinkParameter;
 use TYPO3\TestingFramework\Core\Functional\Framework\Frontend\InternalRequest;
 use TYPO3\TestingFramework\Core\Functional\FunctionalTestCase;
+use TYPO3Fluid\Fluid\View\TemplateView;
 
 final class TypolinkViewHelperTest extends FunctionalTestCase
 {
@@ -46,14 +53,18 @@ final class TypolinkViewHelperTest extends FunctionalTestCase
     protected function setUp(): void
     {
         parent::setUp();
-        $this->importCSVDataSet(__DIR__ . '/../../Fixtures/pages.csv');
-        $this->writeSiteConfiguration(
-            'test',
-            $this->buildSiteConfiguration(1, '/'),
+        $request = new ServerRequest('http://localhost/');
+        $request = $request->withAttribute('applicationType', SystemEnvironmentBuilder::REQUESTTYPE_FE);
+        $request = $request->withAttribute('routing', new PageArguments(1, '0', []));
+        $request = $request->withAttribute('site', new Site(
+            'site',
+            1,
             [
-                $this->buildDefaultLanguageConfiguration('EN', '/en/'),
+                'base' => 'http://localhost/',
+                'languages' => [],
             ]
-        );
+        ));
+        $GLOBALS['TYPO3_REQUEST'] = $request;
     }
 
     public static function renderDataProvider(): array
@@ -114,6 +125,14 @@ final class TypolinkViewHelperTest extends FunctionalTestCase
     #[Test]
     public function render(string $template, string $expected): void
     {
+        $this->importCSVDataSet(__DIR__ . '/../../Fixtures/pages.csv');
+        $this->writeSiteConfiguration(
+            'test',
+            $this->buildSiteConfiguration(1, '/'),
+            [
+                $this->buildDefaultLanguageConfiguration('EN', '/en/'),
+            ]
+        );
         (new ConnectionPool())->getConnectionForTable('sys_template')->insert('sys_template', [
             'pid' => 1,
             'root' => 1,
@@ -135,4 +154,42 @@ EOT
         );
         self::assertStringContainsString($expected, (string)$response->getBody());
     }
+
+    public static function renderWithAssignedParametersDataProvider(): array
+    {
+        return [
+            'parameter' => [
+                '<f:uri.typolink parameter="{parameter}" />',
+                [
+                    'parameter' => 'http://typo3.org/',
+                ],
+                'http://typo3.org/',
+            ],
+            'typolinkParameter object' => [
+                '<f:uri.typolink parameter="{parameter}" />',
+                [
+                    'parameter' => new TypolinkParameter('http://typo3.org/'),
+                ],
+                'http://typo3.org/',
+            ],
+            'invalid parameter' => [
+                '<f:uri.typolink parameter="{parameter}" />',
+                [
+                    'parameter' => new \stdClass(),
+                ],
+                '',
+            ],
+        ];
+    }
+
+    #[DataProvider('renderWithAssignedParametersDataProvider')]
+    #[Test]
+    public function renderWithAssignedParameters(string $template, array $assigns, string $expected): void
+    {
+        $context = $this->get(RenderingContextFactory::class)->create();
+        $context->getTemplatePaths()->setTemplateSource($template);
+        $view = new TemplateView($context);
+        $view->assignMultiple($assigns);
+        self::assertSame($expected, trim($view->render()));
+    }
 }
diff --git a/typo3/sysext/frontend/Classes/Typolink/TypolinkParameter.php b/typo3/sysext/frontend/Classes/Typolink/TypolinkParameter.php
new file mode 100644
index 0000000000000000000000000000000000000000..cbb69e4d1fe4e1f23b72efdaeca33969a07a9cdc
--- /dev/null
+++ b/typo3/sysext/frontend/Classes/Typolink/TypolinkParameter.php
@@ -0,0 +1,68 @@
+<?php
+
+declare(strict_types=1);
+
+/*
+ * This file is part of the TYPO3 CMS project.
+ *
+ * It is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License, either version 2
+ * of the License, or any later version.
+ *
+ * For the full copyright and license information, please read the
+ * LICENSE.txt file that was distributed with this source code.
+ *
+ * The TYPO3 project - inspiring people to share!
+ */
+
+namespace TYPO3\CMS\Frontend\Typolink;
+
+/**
+ * This class represents an object containing the resolved parameters of a typolink
+ */
+readonly class TypolinkParameter implements \JsonSerializable
+{
+    public function __construct(
+        public string $url = '',
+        public string $target = '',
+        public string $class = '',
+        public string $title = '',
+        public string $additionalParams = '',
+        public array $customParams = [],
+    ) {}
+
+    public static function createFromTypolinkParts(array $typoLinkParts): TypolinkParameter
+    {
+        $url = $typoLinkParts['url'] ?? '';
+        $target = $typoLinkParts['target'] ?? '';
+        $class = $typoLinkParts['class'] ?? '';
+        $title = $typoLinkParts['title'] ?? '';
+        $additionalParams = $typoLinkParts['additionalParams'] ?? '';
+        unset($typoLinkParts['url'], $typoLinkParts['target'], $typoLinkParts['class'], $typoLinkParts['title'], $typoLinkParts['additionalParams']);
+
+        return new self(
+            $url,
+            $target,
+            $class,
+            $title,
+            $additionalParams,
+            $typoLinkParts
+        );
+    }
+
+    public function toArray(): array
+    {
+        return array_merge([
+            'url' => $this->url,
+            'target' => $this->target,
+            'class' => $this->class,
+            'title' => $this->title,
+            'additionalParams' => $this->additionalParams,
+        ], $this->customParams);
+    }
+
+    public function jsonSerialize(): array
+    {
+        return $this->toArray();
+    }
+}