Lowercase<StringType>

Converts every character of a string literal type to lowercase inside the type system.

Signature
type Lowercase<S extends string> = intrinsic;
  • Added in TypeScript 4.1

Parameters

ParameterTypeDescription
$SstringA string literal type or a union of them. Non-literal `string` is allowed but the result cannot be resolved to a literal.

Return value

A string literal type with every character lowercasedThe lowercased literal type. Applied to a union it distributes over each member; applied to the unresolved `string` it stays deferred.

Errors

tsc errors if the argument does not satisfy the `string` constraint.

Changelog

  • PHP 4.1Introduced with template literal types. Implemented as a compiler intrinsic calling JavaScript's `toLowerCase`, and not locale aware.

Lowercase is the mirror of Uppercase and shares its implementation strategy: the standard library declares it as type Lowercase<S extends string> = intrinsic; and the compiler runs toLowerCase() on the literal internally. Neither type is written in TypeScript, which is why you cannot reimplement them yourself.

The dominant use is normalising keys that arrive in inconsistent casing. HTTP header names are the classic example: written "Content-Type" in documentation and "content-type" on the wire. Declare the canonical union once, then type the lookup object as Record<Lowercase<Header>, string> so the two representations can never drift. The same shape covers database column names, feature-flag keys, and any protocol that is case-insensitive in practice but exact in your types.

What trips people up

  • It transforms types only. The runtime toLowerCase() call returns a widened string, so an assertion is usually needed to bridge the two.
  • Round-tripping is not guaranteed to be lossless. Uppercase<Lowercase<S>> is not the same type as S unless S was already all-caps — the compiler cannot undo the transformation.
  • No locale handling: it is exactly what JavaScript's method does, nothing more.

Example

Try it yourself
Loading editor…
Press Run to execute the code.

When to use it

  • Type a headers object whose keys must be the lowercased form of a canonical header union.
  • Normalise a union of enum-style names for use as URL slugs or query parameters.
  • Build a case-insensitive lookup map whose keys are derived from a mixed-case union.

Frequently asked questions

How do I lowercase a string literal type?

Use `Lowercase<"Hello">`, which resolves to `"hello"`. It distributes across unions, so `Lowercase<"GET" | "POST">` becomes `"get" | "post"`.

Can I write Uppercase and Lowercase myself in TypeScript?

No. They are compiler intrinsics — the standard library declares them as `= intrinsic` and the transformation happens inside the checker, so there is no equivalent you can express with conditional or mapped types.

Do these types affect the JavaScript that is emitted?

Not at all. They are erased with everything else in the type system; you still call `toLowerCase()` at runtime and typically assert the result as `Lowercase<T>` to keep the literal type.

More examples

A case-insensitive header getter

Callers use the readable canonical spelling while the storage object uses wire casing, and the assertion is the single point where the runtime `toLowerCase()` meets the type-level `Lowercase`. Output: `application/json` then `req-9`.

Example · typescript
type Header = "Content-Type" | "Accept" | "X-Request-Id";
type WireHeaders = Record<Lowercase<Header>, string>;

const wire: WireHeaders = {
  "content-type": "application/json",
  accept: "application/json",
  "x-request-id": "req-9",
};

function getHeader<H extends Header>(name: H): string {
  return wire[name.toLowerCase() as Lowercase<H>];
}

console.log(getHeader("Content-Type"));
console.log(getHeader("X-Request-Id"));
// getHeader("X-Trace"); // Error: not assignable to Header

Deriving slugs from display names

Each value's type is tied to its own key, so writing `Pricing: "/prices"` fails to compile. Sorting the entries keeps the output stable: `FAQ -> /faq`, `Overview -> /overview`, `Pricing -> /pricing`.

Example · typescript
type Section = "Overview" | "Pricing" | "FAQ";
type Slug<S extends string> = `/${Lowercase<S>}`;

type Nav = { [K in Section]: Slug<K> };

const nav: Nav = {
  Overview: "/overview",
  Pricing: "/pricing",
  FAQ: "/faq",
};

const entries = Object.entries(nav).sort(([a], [b]) => a.localeCompare(b));
for (const [label, href] of entries) {
  console.log(`${label} -> ${href}`);
}

Discussion

  • Be the first to comment on this lesson.