How do I cast an FString to TCHAR array?

If the reason you don’t want to use the workaround is that you are sending a format string, and you want to follow the FString with formated vairables. Then what you would need is:

    FString FormattedErrorMessage;
    if (ErrorMessage.Len() > 0)
    {
        int32 Size = snprintf(nullptr, 0, TCHAR_TO_UTF8(*ErrorMessage), \* your variables here*\) + 1;
        char* Buffer = new char[Size];
        snprintf(Buffer, Size, TCHAR_TO_UTF8(*ErrorMessage),  \* your variables here*\);
        FormattedErrorMessage = FString(UTF8_TO_TCHAR(Buffer));
        delete[] Buffer;
    }

    UE_LOG(LogTemp, Error, TEXT("%s"), *FormattedErrorMessage);

This will also work with a variadic template if that’s what you are looking for. That’s how I stumbled on this thread.
in such chase just paste std::forward(args)… into * your variables here*
For example I used it to create myself a template that encapsulates Timeout checking and error loging into a single template call:

template<typename... Args>
static bool OnTimeout_helper(float MaxTime, FDateTime StartTime, const FString& ErrorMessage, Args&&... args)
{
    if ((FDateTime::Now() - StartTime).GetTotalSeconds() <= MaxTime)
    {
        return false;
    }

    FString FormattedErrorMessage;
    if (ErrorMessage.Len() > 0)
    {
        int32 Size = snprintf(nullptr, 0, TCHAR_TO_UTF8(*ErrorMessage), std::forward<Args>(args)...) + 1;
        char* Buffer = new char[Size];
        snprintf(Buffer, Size, TCHAR_TO_UTF8(*ErrorMessage), std::forward<Args>(args)...);
        FormattedErrorMessage = FString(UTF8_TO_TCHAR(Buffer));
        delete[] Buffer;
    }

    UE_LOG(LogTemp, Error, TEXT("%s"), *FormattedErrorMessage);
    return true;
}