@class and its alias @constructor tell TypeScript that a function is meant to be called with new. TypeScript then treats it like a class: this.name = name declares a property, methods added through Counter.prototype see the right this, and calling the function without new is an error (TS2348).
The tag is for code written before class syntax, or code that keeps the constructor-function style. In a new file, a class gives the same checking without a tag.
At a glance
| Use | JSDoc in a .js file | TypeScript in a .ts file |
|---|---|---|
| A constructor function | /**
* @constructor
* @param {string} name
*/
function Counter(name) { this.name = name; } | class Counter {
name: string;
constructor(name: string) { this.name = name; }
} |
Examples
/**
* @constructor
* @param {string} name
*/
export function Counter(name) {
this.name = name;
this.count = 0;
}
/** @returns {number} */
Counter.prototype.increment = function () {
this.count += 1;
return this.count;
};
const visits = new Counter("visits");
visits.increment();export class Counter {
name: string;
count: number;
constructor(name: string) {
this.name = name;
this.count = 0;
}
increment(): number {
this.count += 1;
return this.count;
}
}
const visits = new Counter("visits");
visits.increment();jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /**
* @constructor
* @param {string} name
*/
export function Counter(name) {
this.name = name;
this.count = 0;
}
const visits = Counter("visits");error TS2348: Value of type 'typeof Counter' is not callable. Did you mean to include 'new'?
export { visits };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @param {string} name */
export function Counter(name) {
this.name = name;
this.count = 0;
}
// No error here: without @constructor, TypeScript lets the function be called directly too.
const visits = Counter("visits");
export { visits };jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} What the tag changes
TypeScript already treats a function that assigns to this as a constructor, so properties and new Counter() work without the tag. What @constructor adds is the other half of the check: the function can no longer be called directly. The second and third examples are the same code with and without the tag, and only the tagged one rejects Counter("visits").
Put the constructor's @param tags in the same comment. Methods assigned to Counter.prototype can take their own JSDoc comment with @param and @returns.
Classes instead
A class declaration needs no tag: calling a class without new is already an error at run time and in the checker, and fields, static members, #private fields and extends all work in .js files. The TypeScript tab on the first example shows the class form.
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. Three of them report something different from 5.9.3, the version the page describes; the ✗ on their badges link to their entries 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.
A constructor function marked with @constructor counter.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
counter.js(6,3): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. counter.js(7,3): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. counter.js(16,16): error TS7009: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.
Calling a @constructor function without new counter.js
- TypeScript 5.9.3
counter.js(11,16): error TS2348: Value of type 'typeof Counter' is not callable. Did you mean to include 'new'?
- TypeScript 6.0.3same as 5.9.3
The same errors as 5.9.3.
- TypeScript 7.0.2differs from 5.9.3
counter.js(6,3): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. counter.js(7,3): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
The same function without @constructor: the missing new goes unnoticed counter.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
counter.js(3,3): error TS2683: 'this' implicitly has type 'any' because it does not have a type annotation. counter.js(4,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
- A function that is meant to work both with and without
new(the wayDateandArraydo) cannot use@constructor, because the tag forbids the plain call. @public,@privateand@protectedmisfire in constructor functions: TypeScript 5.9 reports the private property as inaccessible on the line that declares it, inside the constructor. Use aclassif you need access modifiers.- TypeScript reports constructor-function errors only in checked files (
checkJsor// @ts-check). - TypeScript 7.0 no longer treats a
@classor@constructorfunction as a constructor, and no longer followsprototypeassignments; the section Under TypeScript 6.0 and 7.0 shows what 7.0.2 reports for each example above. Aclassdeclaration is checked the same way in every version.
Questions
What is the difference between @class and @constructor?
None for TypeScript: both names produce the same tag. In the JSDoc documentation tool they are also synonyms.
Sources
- JSDoc Reference: @constructor · TypeScript handbook
- Type Checking JavaScript Files: constructor functions · TypeScript handbook
- @class · JSDoc
Last reviewed 21 October 2026.