Type expression

Generic constraints

  • Generics
  • Tested with TypeScript 5.9

A constraint says what a type parameter must be assignable to. In JSDoc it goes in braces between @template and the name: @template {{ updatedAt: Date }} T is TypeScript's T extends { updatedAt: Date }. Inside the function you may then use updatedAt, and callers must pass something that has it.

Constraints can refer to earlier type parameters. @template T followed by @template {keyof T} K gives the classic property getter, where the key must be one of the object's own keys (TS2345 for "colr", as the first example shows).

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
A constraint /** @template {{ updatedAt: Date }} T */ <T extends { updatedAt: Date }>
Depending on another parameter /** * @template T * @template {keyof T} K */ <T, K extends keyof T>

Examples

pick.js pick.ts Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: pick.js(15,11): error TS2345: Argument of type '"colr"' is not assignable to parameter of type '"price" | "sku"'. pick.ts(8,11): error TS2345: Argument of type '"colr"' is not assignable to parameter of type '"price" | "sku"'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. pick.js(15,11): error TS2345: Argument of type '"colr"' is not assignable to parameter of type '"price" | "sku"'. pick.ts(8,11): error TS2345: Argument of type '"colr"' is not assignable to parameter of type '"price" | "sku"'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. pick.js(15,11): error TS2345: Argument of type '"colr"' is not assignable to parameter of type '"price" | "sku"'. pick.ts(8,11): error TS2345: Argument of type '"colr"' is not assignable to parameter of type '"price" | "sku"'. checked 21 October 2026
/**
 * @template T
 * @template {keyof T} K
 * @param {T} object
 * @param {K} key
 * @returns {T[K]}
 */
export function prop(object, key) {
  return object[key];
}

const tee = { sku: "TEE-01", price: 19.5 };
prop(tee, "price").toFixed(2);
prop(tee, "colr");error TS2345: Argument of type '"colr"' is not assignable to parameter of type '"price" | "sku"'.
export function prop<T, K extends keyof T>(object: T, key: K): T[K] {
  return object[key];
}

const tee = { sku: "TEE-01", price: 19.5 };
prop(tee, "price").toFixed(2);
prop(tee, "colr");error TS2345: Argument of type '"colr"' is not assignable to parameter of type '"price" | "sku"'.
A constraint that uses another type parameter.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
latest.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: latest.js(12,24): error TS2322: Type 'string' is not assignable to type 'Date'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. latest.js(12,24): error TS2322: Type 'string' is not assignable to type 'Date'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. latest.js(12,24): error TS2322: Type 'string' is not assignable to type 'Date'. checked 21 October 2026
/**
 * @template {{ updatedAt: Date }} T
 * @param {T[]} records
 * @returns {T | undefined}
 */
export function latest(records) {
  return [...records].sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime())[0];
}

latest([{ id: "A-100", updatedAt: new Date() }])?.id;
latest([{ id: "A-101", updatedAt: "2026-10-20" }]);error TS2322: Type 'string' is not assignable to type 'Date'.
A constraint lets the body use what it requires.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
pair.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: pair.js(12,7): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. pair.js(12,7): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. pair.js(12,7): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. checked 21 October 2026
/**
 * @template {string} K, V
 * @param {K} key
 * @param {V} value
 */
export function entry(key, value) {
  return { [key]: value };
}

entry("sku", 42);
entry(42, "sku");error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
Only the first name after a constraint is constrained.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

One name per constraint

A constraint applies only to the first name after it. In @template {string} K, V, K must be a string and V is unconstrained, which is easy to misread. The third example shows the first argument checked against string and the second accepting a number. Put each constrained parameter on its own @template line.

Constraints keep the specific type

A constrained parameter still infers the caller's full type. latest(records) in the second example returns the records' own type, id included, not just { updatedAt: Date }. That is the difference from a plain parameter type.

Gotchas

  • When an argument does not meet the constraint, the error may be reported on a property of the argument rather than on the call, as with updatedAt: "2026-10-20" (TS2322).
  • A constraint and a default can be combined: @template {string | number} [T=number].

Questions

How do I write T extends something in JSDoc?

Put the constraint in braces before the name: @template {Something} T.

Sources

Last reviewed 20 October 2026.