Type expression

Closure-style JSDoc types

  • Legacy syntax
  • Tested with TypeScript 5.9

Much older JavaScript documents types in the style of Google's Closure Compiler. TypeScript reads most of that syntax in .js files, so existing comments keep working: {?string} is string | null, {Array.<string>} is string[], {function(string): boolean} is a function type. The table below lists each form with its TypeScript meaning.

Two forms do not mean what they did in Closure. {!Type} is read as plain Type, so it does not strip null (the second example fails with TS2322 under strict). And the postfix = works for parameters but not for properties inside an object type, where it is a syntax error.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
Nullable {?string} string | null
Non-nullable (no effect) {!string} string
Any type {*} or {?} any
Arrays {Array.<string>} string[]
Dictionaries {Object.<string, number>} { [x: string]: number }
Functions {function(string, number=): boolean} (sku: string, min?: number) => boolean
Constructors {function(new: Date, string)} new (value: string) => Date
Optional parameter @param {string=} label label?: string
Rest parameter @param {...number} prices ...prices: number[]

Examples

legacy.js legacy.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 : differs, see why TypeScript 7.0.2, run 21 October 2026: differs from 5.9.3. legacy.js(13,20): error TS1005: '}' expected. legacy.js(14,18): error TS7006: Parameter 'sku' implicitly has an 'any' type. legacy.js(14,23): error TS7006: Parameter 'min' implicitly has an 'any' type. legacy.js(16,20): error TS1005: '}' expected. legacy.js(30,5): error TS2351: This expression is not constructable. Type 'Function' has no construct signatures. checked 21 October 2026
/** @type {?string} */
let coupon = null;

/** @type {*} */
let anything = 1;

/** @type {Array.<string>} */
const sizes = ["S", "M"];

/** @type {Object.<string, number>} */
const stock = { "TEE-01": 12 };

/** @type {function(string, number=): boolean} */
const inStock = (sku, min) => (stock[sku] ?? 0) >= (min ?? 1);

/** @type {function(new: Date, string)} */
const DateFrom = Date;

/**
 * @param {string=} label
 * @param {...number} prices
 */
export function summary(label, ...prices) {
  return `${label ?? "Total"}: ${prices.reduce((a, b) => a + b, 0)}`;
}

coupon = "SPRING";
anything = "two";
inStock("TEE-01");
new DateFrom("2026-10-20").getFullYear();
summary(undefined, 19.5, 12);

export { coupon, anything, sizes };
let coupon: string | null = null;

let anything: any = 1;

const sizes: Array<string> = ["S", "M"];

const stock: { [x: string]: number } = { "TEE-01": 12 };

const inStock: (sku: string, min?: number) => boolean = (sku, min) => (stock[sku] ?? 0) >= (min ?? 1);

const DateFrom: new (value: string) => Date = Date;

export function summary(label?: string, ...prices: number[]) {
  return `${label ?? "Total"}: ${prices.reduce((a, b) => a + b, 0)}`;
}

coupon = "SPRING";
anything = "two";
inStock("TEE-01");
new DateFrom("2026-10-20").getFullYear();
summary(undefined, 19.5, 12);

export { coupon, anything, sizes };
Closure-style types TypeScript still accepts.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
legacy.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: legacy.js(3,7): error TS2322: Type 'HTMLAnchorElement | null' is not assignable to type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. legacy.js(3,7): error TS2322: Type 'HTMLAnchorElement | null' is not assignable to type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. legacy.js(3,7): error TS2322: Type 'HTMLAnchorElement | null' is not assignable to type 'HTMLElement'. Type 'null' is not assignable to type 'HTMLElement'. checked 21 October 2026
/** @type {!HTMLElement} */
const link = document.querySelector("a");error TS2322: Type 'HTMLAnchorElement | null' is not assignable to type 'HTMLElement'.
  Type 'null' is not assignable to type 'HTMLElement'.

export { link };
!Type does not remove null.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
legacy.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: legacy.js(5,12): error TS2552: Cannot find name 'array'. Did you mean 'Array'? legacy.js(9,13): error TS2552: Cannot find name 'promise'. Did you mean 'Promise'? 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. legacy.js(5,12): error TS2552: Cannot find name 'array'. Did you mean 'Array'? legacy.js(9,13): error TS2552: Cannot find name 'promise'. Did you mean 'Promise'? 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. legacy.js(5,12): error TS2552: Cannot find name 'array'. Did you mean 'Array'? legacy.js(9,13): error TS2552: Cannot find name 'promise'. Did you mean 'Promise'? checked 21 October 2026
/** @type {Array<string>} */
const tags = [];

/** @type {array} */error TS2552: Cannot find name 'array'. Did you mean 'Array'?
const legacyTags = [];

/** @param {promise} pending */error TS2552: Cannot find name 'promise'. Did you mean 'Promise'?
export function settle(pending) {
  return pending;
}

export { tags, legacyTags };
Lowercase array and promise are not types under noImplicitAny.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Old type names

TypeScript maps some names from older JSDoc to its own: String, Number, Boolean, Void, Undefined and Null to the lowercase types, function to Function. The handbook lists array, promise, Object and object as well, mapped to any-based types, but only while noImplicitAny is off. With strict, lowercase array and promise are TS2552 ("Cannot find name 'array'. Did you mean 'Array'?"), as the third example shows.

Modernizing old comments

There is no need to rewrite working comments in one pass. Convert them as you touch the code: ?string to string | null, Array.<T> to T[], function(…) to arrow syntax. TypeScript's own messages print the modern forms, so matching them makes errors easier to read.

Under TypeScript 6.0 and 7.0

Every example on this page also runs under TypeScript 6.0.3 and 7.0.2 when this site is built. One of them reports something different from 5.9.3, the version the page describes; the ✗ on its badge links to its entry below.

Closure function types do not parse

TypeScript 7.0 · JSDoc and JavaScript patterns 7.0 no longer reads

The pattern: {function(string): boolean}, {function(number, string=): string} and {function(new: Date, string)} in JSDoc.

Why: The port's list of changes removes Closure function types from the JSDoc parser. The type is a syntax error (TS1005), so the parameters it should type are untyped (TS7006 under strict) and calls through it fail (TS2769, TS2351). Source: CHANGES.md: changes between TypeScript 6 and the native port.

Closure-style types TypeScript still accepts legacy.js

TypeScript 5.9.3

No errors.

TypeScript 6.0.3same as 5.9.3

No errors.

TypeScript 7.0.2differs from 5.9.3
legacy.js(13,20): error TS1005: '}' expected.
legacy.js(14,18): error TS7006: Parameter 'sku' implicitly has an 'any' type.
legacy.js(14,23): error TS7006: Parameter 'min' implicitly has an 'any' type.
legacy.js(16,20): error TS1005: '}' expected.
legacy.js(30,5): error TS2351: This expression is not constructable.
  Type 'Function' has no construct signatures.

What works in every version: Arrow syntax: {(sku: string) => boolean}, {(amount: number, currency?: string) => string}, {new (iso: string) => Date}.

sort.js sort.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 {{ sku: string, price: number }} Product */

/** @type {(amount: number, currency?: string) => string} */
export const formatPrice = (amount, currency = "USD") =>
  new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount);

/**
 * @param {Product[]} products
 * @param {(a: Product, b: Product) => number} compare
 */
export function sorted(products, compare) {
  return [...products].sort(compare);
}

sorted([{ sku: "TEE-01", price: 19.5 }], (a, b) => a.price - b.price);
formatPrice(19.5);
type Product = { sku: string; price: number };

export const formatPrice: (amount: number, currency?: string) => string = (amount, currency = "USD") =>
  new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount);

export function sorted(products: Product[], compare: (a: Product, b: Product) => number) {
  return [...products].sort(compare);
}

sorted([{ sku: "TEE-01", price: 19.5 }], (a, b) => a.price - b.price);
formatPrice(19.5);
Function types in arrow syntax.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Gotchas

  • {?} and {*} both mean any, not unknown; they turn checking off.
  • {?string} only adds null; a value that may be undefined needs {string | undefined}.
  • Closure's record type {{a: number}} is the same as a TypeScript object type and needs no change.
  • TypeScript 7.0 drops most of these forms: {?}, Closure function types and the postfix ! are not read there (TypeScript 7.0; the section Under TypeScript 6.0 and 7.0 has what 7.0.2 reports for the examples above). The TypeScript spellings in the table work in every version.

Questions

Does TypeScript support Closure Compiler JSDoc syntax?

Mostly. Nullable ?T, *, Array.<T>, Object.<K, V>, function(…) types and the = and ... parameter forms all work in .js files; !T is accepted but ignored.

Sources

Last reviewed 21 October 2026.