I would like to create a Type that will ensure that a string starts with an html-tag and closes with the same one. Currently I do not get the opening and closing tag to match. Is there some infer or other Typescript magic I can use?
type Template<
TAG extends string,
CONTENT extends string,
ATTRIBUTES extends string
> = `<${TAG}${ATTRIBUTES}>${CONTENT}</${TAG}>`;
export type HtmlTemplate<HTML extends string> = Template<`${keyof HTMLElementTagNameMap}`, HTML, ` ${string}`>;
export type UliConfigTemplate = `${StyleConfig<string>} ${TemplateConfig<string>}`;
The current version essentially produces the following type definition (see the tags are not matching):
type TemplateConfig<HTML extends string> = `<object ${string}>${HTML}</object>` | `<object ${string}>${HTML}</a>` | `<object ${string}>${HTML}</abbr>` | `<object ${string}>${HTML}</address>` | `<object ${string}>${HTML}</applet>` | `<object ${string}>${HTML}</area>` | `<object ${string}>${HTML}</article>` | `<object ${string}>${HTML}</aside>` | `<object ${string}>${HTML}</audio>` | ... 14151 more ... | `<wbr ${string}>${HTML}</wbr>````
You can do it. TAG
type in your example is a union like 'object' | 'a' | 'abbr' | ...
. What you need is to "map" every member of this union to its own template literal, then get the union of all these mapped template literals. This can be done like this:
type Template<
TTag extends string,
TContent extends string,
TAttributes extends string,
> = {
// mapping every tag to its own template literal
[tag in TTag]: `<${tag}${TAttributes}>${TContent}</${tag}>`
}[TTag]
export type HtmlTemplate<HTML extends string> = Template<
keyof HTMLElementTagNameMap,
HTML,
` ${string}`
>;
Now it has type
`<object ${string}>${HTML}</object>` | `<a ${string}>${HTML}</a>`
| `<abbr ${string}>${HTML}</abbr>` | `<address ${string}>${HTML}</address>`
| `<area ${string}>${HTML}</area>` | `<article ${string}>${HTML}</article>`
| `<aside ${string}>${HTML}</aside>` | `<audio ${string}>${HTML}</audio>` | `<b ${string}>${HTML}</b>`
| ... 107 more ... | `<wbr ${string}>${HTML}</wbr>`