JSDoc tag

@property

  • Checked by TypeScript
  • Tested with TypeScript 5.9
  • Alias @prop

@property {Type} name (or @prop) declares one property of the object type a @typedef names. The lines together build the type, much like the members of an interface, and each line's text becomes the property's description in editor hovers.

TypeScript reads @property only inside a @typedef block. In the JSDoc comment of a class it is ignored, as the third example shows; declare class properties in the class body instead.

At a glance

Use JSDoc in a .js file TypeScript in a .ts file
A property of a @typedef /** * @typedef {object} Address * @property {string} street */ interface Address { street: string; }
Optional * @property {string} [unit] unit?: string;
Nested * @property {object} geo * @property {number} geo.lat geo: { lat: number };

Examples

address.js address.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 {object} Address
 * @property {string} street
 * @property {string} [unit] apartment or suite
 * @property {string} city
 * @prop {string} postalCode
 * @property {object} [geo]
 * @property {number} geo.lat
 * @property {number} geo.lng
 */

/** @type {Address} */
const office = {
  street: "12 Market St",
  city: "Springfield",
  postalCode: "62701",
  geo: { lat: 39.8, lng: -89.65 },
};

/** @param {Address} address */
export function label(address) {
  return [address.street, address.unit, address.city].filter(Boolean).join(", ");
}

label(office);
interface Address {
  street: string;
  /** apartment or suite */
  unit?: string;
  city: string;
  postalCode: string;
  geo?: { lat: number; lng: number };
}

const office: Address = {
  street: "12 Market St",
  city: "Springfield",
  postalCode: "62701",
  geo: { lat: 39.8, lng: -89.65 },
};

export function label(address: Address) {
  return [address.street, address.unit, address.city].filter(Boolean).join(", ");
}

label(office);
Describing an object type with @typedef and @property.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
address.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: address.js(10,10): error TS18048: 'address.unit' is possibly 'undefined'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. address.js(10,10): error TS18048: 'address.unit' is possibly 'undefined'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. address.js(10,10): error TS18048: 'address.unit' is possibly 'undefined'. checked 21 October 2026
/**
 * @typedef {object} Address
 * @property {string} street
 * @property {string} [unit]
 */

/** @param {Address} address */
export function unitLabel(address) {
  return address.unit.toUpperCase();error TS18048: 'address.unit' is possibly 'undefined'.
}
An optional @property has to be checked before use.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}
product.js Error confirmed 5.9.3 TypeScript 5.9.3, run 21 October 2026: product.js(7,15): error TS2339: Property 'sku' does not exist on type 'Product'. 6.0.3 TypeScript 6.0.3, run 21 October 2026: the same as 5.9.3. product.js(7,15): error TS2339: Property 'sku' does not exist on type 'Product'. 7.0.2 TypeScript 7.0.2, run 21 October 2026: the same as 5.9.3. product.js(7,15): error TS2339: Property 'sku' does not exist on type 'Product'. checked 21 October 2026
/**
 * @property {string} sku
 */
export class Product {}

new Product().sku;error TS2339: Property 'sku' does not exist on type 'Product'.
@property in a class comment is not read.
jsconfig.json for this example
{
  "compilerOptions": {
    "checkJs": true,
    "noEmit": true,
    "strict": true,
    "target": "es2022",
    "module": "nodenext",
    "types": []
  },
  "include": ["*.js"]
}

Optional and nested properties

Brackets make a property optional: @property {string} [unit] has the type string | undefined when read, so it must be checked before use (TS18048 otherwise, as the second example shows).

Dotted names describe an object inside the object: @property {object} [geo] followed by @property {number} geo.lat. For anything deeper, a separate @typedef for the inner object is easier to read.

The one-line alternative

/** @typedef {{ street: string, unit?: string }} Address */ declares the same type without @property lines. The multi-line form is worth it when properties need descriptions.

Gotchas

  • Another tag in the middle of the @property lines ends the type there. A @readonly line, for example, drops every property after it; see the @readonly page.
  • @property on a class comment is ignored: /** @property {string} sku */ class Product {} adds nothing, and new Product().sku is TS2339.

Questions

What is the difference between @property and @prop?

None. TypeScript treats them as the same tag.

Sources

Last reviewed 20 October 2026.