[API reference](https://evolu.dev/docs/api-reference) › [@evolu/common](https://evolu.dev/docs/api-reference/common) › [Type](https://evolu.dev/docs/api-reference/common/Type) › templateLiteralParser

```ts
function templateLiteralParser<Parts>(
  ...parts: {
    readonly [Index in string | number | symbol]: ValidateTemplateLiteralPart<
      Parts[Index]
    >;
  } & TemplateLiteralValidation<Parts>
): TemplateLiteralParserType<Parts>;
```

Defined in: [packages/common/src/Type.ts:4155](https://github.com/evoluhq/evolu/blob/037c390af081e9944d616ff298a729515c5c5ab7/packages/common/src/Type.ts#L4155)

Template literal [Type](https://evolu.dev/docs/api-reference/common/Type/interfaces/Type) that parses canonical strings into Tuples.

Accepts the same template parts as [templateLiteral](https://evolu.dev/docs/api-reference/common/Type/functions/templateLiteral): fixed string
literals and Types canonically encoded as strings. Instead of keeping Output
as a string, fixed literals define the framing and Output is a readonly Tuple
of the decoded Type parts. `to` encodes that Tuple back into the canonical
string represented by the parent Type. At least one Type part is required.

When every capture uses identity encoding, the parent Output is the exact
TypeScript template literal type. A transforming capture makes it nominal;
create such strings with `to` or validate them with the parent Type.

Deterministic framing is a core correctness guarantee. It preserves
reversibility and keeps capture boundaries unambiguous. Different capture
Tuples must never encode to the same string. The parser provides predictable
parsing without pathological backtracking and decodes each capture once, so
adversarial input cannot trigger exponential parser work. Fixed-width
captures may be adjacent, but only one variable-width capture is allowed.
Declarations that could join UTF-16 surrogate halves across parts are
rejected during construction.

Keep capture unions reasonably small to avoid excessive compiler work.

TypeScript template literal types can describe a fixed number of digit
positions, but not an arbitrarily long sequence of digits. Such grammars use
branded Types such as [DecimalString](https://evolu.dev/docs/api-reference/common/Type/variables/DecimalString); `templateLiteralParser` preserves
that exactness by requiring a validated branded capture when encoding.

### Example

A template literal Type defines both a canonical string representation and
the structured data decoded from it:

```ts
import {
  assertFalse,
  assertEqual,
  assertErr,
  assertOk,
  assertType,
  Data,
  templateLiteralParser,
  union,
} from "@evolu/common";

const Language = union("en", "cs");
const Region = union("US", "CZ");

// Define a Type for "en-US" | "en-CZ" | "cs-US" | "cs-CZ".
const SupportedLocale = templateLiteralParser(Language, "-", Region);

// Output is the decoded language and region.
type SupportedLocale = typeof SupportedLocale.Output;
assertType<readonly ["en" | "cs", "US" | "CZ"], SupportedLocale>();

// The parent Output is the canonical locale string.
type SupportedLocaleLiteral = typeof SupportedLocale.parent.Output;
assertType<"en-US" | "en-CZ" | "cs-US" | "cs-CZ", SupportedLocaleLiteral>();

// Parse an unknown string into structured data.
const result = SupportedLocale.fromUnknown("cs-CZ");
assertOk(result, ["cs", "CZ"]);
const locale = result.value;
assertType<SupportedLocale, typeof locale>();
const invalid = SupportedLocale.fromUnknown("cs/CZ");
assertErr(invalid);
assertType(Data, invalid.error);
const error: Data = invalid.error;
assertEqual(error, {
  type: "TemplateLiteral",
  value: "cs/CZ",
});

// Encode structured data into its canonical string.
const localeLiteral = SupportedLocale.to(locale);
assertType<SupportedLocaleLiteral, typeof localeLiteral>();
assertEqual(localeLiteral, "cs-CZ");

// Validate a string configuration value.
const configValue: unknown = "cs-CZ";
assertType(SupportedLocale.parent, configValue);
assertType<SupportedLocaleLiteral, typeof configValue>();
assertFalse(SupportedLocale.parent.is("fr-CZ"));
```

`SupportedLocale` is structured data for application code.
`SupportedLocaleLiteral` is its canonical representation for configuration
and other APIs that require a string, such as URL parameters, environment
variables, and storage keys.

Use branded captures for strings that TypeScript template literal types
cannot express exactly, such as arbitrary-length canonical decimals:

```ts
import {
  assertEqual,
  assertOk,
  NonNegativeDecimalString,
  templateLiteralParser,
} from "@evolu/common";

const DecimalText = templateLiteralParser("decimal:", NonNegativeDecimalString);

// DecimalText.to requires a validated NonNegativeDecimalString.
const zero = NonNegativeDecimalString.orThrow("0");

assertOk(DecimalText.fromUnknown("decimal:0"), [zero]);
assertEqual(DecimalText.to([zero]), "decimal:0");
```

Capture Types (the Type arguments passed to `templateLiteralParser`) can use
transformations to decode substrings into non-string data:

```ts
import {
  assertEqual,
  assertOk,
  assertType,
  Int64FromInt64String,
  templateLiteralParser,
} from "@evolu/common";

const ItemId = templateLiteralParser("item-", Int64FromInt64String);
type ItemId = typeof ItemId.Output;
type ItemIdLiteral = typeof ItemId.parent.Output;

// Decode the string into structured data.
const result = ItemId.fromUnknown("item-42");
assertOk(result, [42n]);
const itemId = result.value;
assertType<ItemId, typeof itemId>();

// Encode the structured data into its canonical string.
const itemIdLiteral = ItemId.to(itemId);
assertType<ItemIdLiteral, typeof itemIdLiteral>();
assertEqual(itemIdLiteral, "item-42");

// TypeScript cannot prove from the literal alone that "42" is a valid Int64 encoding.
// @ts-expect-error Validate it with ItemId.parent or create it with ItemId.to.
const _invalidItemIdLiteral: ItemIdLiteral = "item-42";
```

Fixed-width captures can be adjacent:

```ts
import {
  assertEqual,
  assertOk,
  templateLiteralParser,
  union,
} from "@evolu/common";

const Digit = union("0", "1", "2", "3", "4", "5", "6", "7", "8", "9");
const TwoDigits = templateLiteralParser(Digit, Digit);
type TwoDigits = typeof TwoDigits.Output;
type TwoDigitsLiteral = typeof TwoDigits.parent.Output;

const twoDigits: TwoDigits = ["4", "2"];
const twoDigitsLiteral: TwoDigitsLiteral = "42";
// @ts-expect-error TwoDigitsLiteral requires exactly two digits.
const _threeDigitsLiteral: TwoDigitsLiteral = "123";

assertOk(TwoDigits.from.parent(twoDigitsLiteral), twoDigits);
assertEqual(TwoDigits.to(twoDigits), twoDigitsLiteral);
```

TypeScript rejects multiple variable-width captures because their encoded
boundaries would be ambiguous:

```ts

// @ts-expect-error At most one Type capture can have a variable-width string representation.
templateLiteralParser(String, ":", String);
```

This restriction keeps encoding reversible: different capture Tuples must
never produce the same string. A delimiter alone is not enough because it can
also occur inside a capture. Some formats could provide stronger guarantees,
such as captures that exclude a delimiter; support for those can be added
when concrete use cases justify the additional framing rules.