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
/** @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`'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @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 };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @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}`'.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
stringvalue 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
- Template Literal Types · TypeScript handbook
- TypeScript 4.1: template literal types · TypeScript release notes
Last reviewed 20 October 2026.