Type expression

Template literal types

  • Combining types
  • Tested with TypeScript 5.9

A template literal type uses the syntax of a JavaScript template string to describe a set of strings: `${number}px` accepts "320px" and rejects "20em" (TS2345). TypeScript added them in version 4.1, and they work inside JSDoc braces unchanged.

Placeholders can hold unions, and every combination is produced: `${"TEE" | "MUG"}-${number}` accepts "TEE-01" and "MUG-7" but not "tee-01", as the third example shows.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
A pattern /** @typedef {`${number}px`} Pixels */ type Pixels = `${number}px`;
Built from a union /** @typedef {`on${Capitalize<Interaction>}`} HandlerName */ type HandlerName = `on${Capitalize<Interaction>}`;

Examples

style.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: style.js(10,10): error TS2345: Argument of type '"20em"' is not assignable to parameter of type '`${number}px`'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. style.js(10,10): error TS2345: Argument of type '"20em"' is not assignable to parameter of type '`${number}px`'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. style.js(10,10): error TS2345: Argument of type '"20em"' is not assignable to parameter of type '`${number}px`'. checked 21 October 2026
/** @typedef {`${number}px`} Pixels */

/** @param {Pixels} width */
export function setWidth(width) {
  document.body.style.width = width;
}

setWidth("320px");
setWidth("20em");error TS2345: Argument of type '"20em"' is not assignable to parameter of type '`${number}px`'.
A string pattern as a type.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
handlers.js handlers.ts TypeScript 5.9.3 TypeScript 5.9.3, run 21 October 2026: No errors. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. No errors. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. No errors. checked 21 October 2026
/** @typedef {"click" | "hover" | "focus"} Interaction */
/** @typedef {`on${Capitalize<Interaction>}`} HandlerName */

/** @type {Record<HandlerName, () => void>} */
const handlers = {
  onClick: () => {},
  onHover: () => {},
  onFocus: () => {},
};

export { handlers };
type Interaction = "click" | "hover" | "focus";
type HandlerName = `on${Capitalize<Interaction>}`;

const handlers: Record<HandlerName, () => void> = {
  onClick: () => {},
  onHover: () => {},
  onFocus: () => {},
};

export { handlers };
Building names from a union.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
sku.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: sku.js(10,12): error TS2345: Argument of type '"tee-01"' is not assignable to parameter of type '`TEE-${number}` | `MUG-${number}` | `CAP-${number}`'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. sku.js(10,12): error TS2345: Argument of type '"tee-01"' is not assignable to parameter of type '`TEE-${number}` | `MUG-${number}` | `CAP-${number}`'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. sku.js(10,12): error TS2345: Argument of type '"tee-01"' is not assignable to parameter of type '`CAP-${number}` | `MUG-${number}` | `TEE-${number}`'. checked 21 October 2026
/** @typedef {`${"TEE" | "MUG" | "CAP"}-${number}`} Sku */

/** @param {Sku} sku */
export function productUrl(sku) {
  return `/products/${sku.toLowerCase()}`;
}

productUrl("TEE-01");
productUrl("tee-01");error TS2345: Argument of type '"tee-01"' is not assignable to parameter of type '`TEE-${number}` | `MUG-${number}` | `CAP-${number}`'.
A SKU format checked at every call.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Changing case

The built-in Uppercase, Lowercase, Capitalize and Uncapitalize types change the case of string literal types. `on${Capitalize<Interaction>}` turns "click" | "hover" into "onClick" | "onHover", which keeps handler names and event names in step.

Gotchas

  • A plain string value does not match a pattern even if its contents would; the check needs a literal or a value already typed with the pattern.
  • Large unions in several placeholders multiply: three placeholders of ten members each make a thousand strings, which slows checking.

Questions

Do template literal types work in JSDoc?

Yes. Write them with backticks inside the braces, exactly as in TypeScript: /** @type {`${number}px`} */.

Sources

Last reviewed 20 October 2026.