@private on a class member limits it to code inside that class. Since TypeScript 3.8, a use from outside, including from a subclass, is an error (TS2341, "Property 'balance' is private and only accessible within class 'Account'"). It is the JavaScript spelling of TypeScript's private modifier.
Like private, the tag is a compile-time check only. The property is an ordinary property at run time, visible to Object.keys and to any code that ignores the types. A #private field is private at run time as well and needs no tag.
At a glance
| Use | JSDoc in a .js file | TypeScript in a .ts file |
|---|---|---|
| A private property | /** @private */
balance = 0; | private balance = 0; |
| Private at run time too | #balance = 0; | #balance = 0; |
Examples
export class Account {
/** @private */
balance = 0;
/** @param {number} amount */
deposit(amount) {
this.balance += amount;
return this.balance;
}
}
const account = new Account();
account.deposit(20);
account.balance = 1000000;error TS2341: Property 'balance' is private and only accessible within class 'Account'.export class Account {
private balance = 0;
deposit(amount: number) {
this.balance += amount;
return this.balance;
}
}
const account = new Account();
account.deposit(20);
account.balance = 1000000;error TS2341: Property 'balance' is private and only accessible within class 'Account'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} export class Account {
#balance = 0;
/** @param {number} amount */
deposit(amount) {
this.#balance += amount;
return this.#balance;
}
get balance() {
return this.#balance;
}
}
const account = new Account();
account.deposit(20);
account.balance;jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @constructor */
export function Account() {
/** @private */
this.balance = 0;error TS2341: Property 'balance' is private and only accessible within class 'Account'.
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} @private or a #private field?
A #balance field cannot be read from outside the class at all, by any code, so it is the stronger choice for real encapsulation. The @private tag fits code that must keep a plain property name: properties read by serialization, tests that peek on purpose, or older code where renaming would break callers.
An accessibility tag on a # field is an error (TS18010), since the field is already private.
Not in constructor functions
The handbook says the accessibility tags do not work in constructor functions, and the third example shows what happens: TypeScript 5.9 reports TS2341 on the line inside the constructor that creates the property, the one place it should be allowed. Convert the function to a class before adding @private.
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.
Constructor functions are not classes
The pattern: A function that sets this.name = … and is called with new, with or without @class or @constructor, and methods added through prototype.
Why: The port's list of changes says constructor functions are no longer supported and @class or @constructor no longer makes a function a constructor. So this in the function is untyped (TS2683 under strict), new on it is untyped (TS7009), and the checks that came with the class (TS2348 for a call without new, TS2341 for a @private property) are gone. Source: CHANGES.md: Constructor functions are no longer supported.
@private in a constructor function reports the wrong line account.js
- TypeScript 5.9.3
account.js(5,8): error TS2341: Property 'balance' is private and only accessible within class 'Account'.
- TypeScript 6.0.3same as 5.9.3
The same errors as 5.9.3.
- TypeScript 7.0.2differs from 5.9.3
account.js(5,3): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
What works in every version: Write a class, with fields and methods in its body.
/** @typedef {{ sku: string, quantity: number }} Line */
export class Cart {
/** @type {Line[]} */
items = [];
/** @type {string | null} */
coupon = null;
/**
* @param {string} currency ISO code, such as "USD".
*/
constructor(currency) {
this.currency = currency;
}
/**
* @param {string} sku
* @param {number} [quantity]
*/
add(sku, quantity = 1) {
this.items.push({ sku, quantity });
return this;
}
}
const cart = new Cart("USD").add("LAMP-01").add("DESK-02", 2);
console.log(cart.currency.toLowerCase(), cart.items.length, cart.coupon?.trim());type Line = { sku: string; quantity: number };
export class Cart {
items: Line[] = [];
coupon: string | null = null;
currency: string;
constructor(currency: string) {
this.currency = currency;
}
add(sku: string, quantity = 1): this {
this.items.push({ sku, quantity });
return this;
}
}
const cart = new Cart("USD").add("LAMP-01").add("DESK-02", 2);
console.log(cart.currency.toLowerCase(), cart.items.length, cart.coupon?.trim());jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Gotchas
- The
privatekeyword is TypeScript syntax; in a.jsfile it is an error (TS8009). @privatealso blocks subclasses. For members subclasses may use, write@protected.
Questions
Does @private make a property private at run time?
No. It only changes what TypeScript allows. Use a #private field when the value must be hidden at run time.
Sources
- TypeScript 3.8: JSDoc property modifiers · TypeScript release notes
- JSDoc Reference: property modifiers · TypeScript handbook
- @private · JSDoc
Last reviewed 20 October 2026.