This is a tricky problem, primarily around trying to get a good developer experience.
My first thought was H2 to H6 components, since we can then restrict title the way you say you want to (which I don't think we can with "h2" to "h6" tags). That seemed really promising until the dev experience, which turned out to be really disappointing. (I've included it below just for reference.)
My second thought was that title could be a {tag: HeaderType; text: string;} object type, but that's no better from a devex point of view than what you had and what Mike suggested.
But React gets really good use of tuples, and the devex of making title a tuple seems decent:

Here's the implementation:
type HeaderType = "h2" | "h3" | "h4" | "h5" | "h6";
type Title = [tag: HeaderType, text: ReactNode];
interface OwnProps {
title?: Title;
}
Using it on a Section component:
const Section = ({ title, children }: PropsWithChildren<OwnProps>) => {
return <section>
{title && createElement(title[0], { children: title[1] })}
{children}
</section>;
};
Playground link
The component solution was this:
If it were components we were trying to do this for, we could readily do it as:
type HeaderType =
| React.ReactElement<typeof ThisComponent>
| React.ReactElement<typeof ThatComponent>
| React.ReactElement<typeof TheOtherComponent>
// ...
So we could make H2 through H6 components. We could do it by just repeating ourselves:
const H2 = (props: ComponentProps<"h2">) => <h2 {...props}/>;
const H3 = (props: ComponentProps<"h3">) => <h3 {...props}/>;
// ....
...but even though there are only five h2...h6 tags, maybe we'll want to do this for other tags as well. So maybe a utility function:
const componentFor = <Tag extends keyof JSX.IntrinsicElements>(tag: Tag) =>
(props: ComponentProps<Tag>) => React.createElement(tag, props);
Then defining these (and perhaps others some other time) is quite simple:
const H2 = componentFor("h2");
const H3 = componentFor("h3");
const H4 = componentFor("h4");
const H5 = componentFor("h5");
const H6 = componentFor("h6");
Then our HeaderType is:
type HeaderType =
| React.ReactElement<typeof H2>
| React.ReactElement<typeof H3>
| React.ReactElement<typeof H4>
| React.ReactElement<typeof H5>
| React.ReactElement<typeof H6>;
But the devex isn't that great, the auto-complete just shows all components in scope:

Playground link