When a function takes a class as an argument, the parameter's type is a constructor type: {new (sku: string) => T} means "something you can call with new and a string, which produces a T". Any class with a compatible constructor fits, and the result of new Kind(…) is typed T.
{typeof Shirt} is the constructor type of one particular class, static members included. Use it when the code needs Shirt's statics, such as Kind.category in the second example.
At a glance
| Use | JSDoc in a .js file | TypeScript in a .ts file |
|---|---|---|
| Something you call with new | /** @param {new (sku: string) => Shirt} Kind */ | (Kind: new (sku: string) => Shirt) |
| A specific class | /** @param {typeof Shirt} Kind */ | (Kind: typeof Shirt) |
| Closure spelling | {function(new: Shirt, string)} | new (sku: string) => Shirt |
Examples
/**
* @template T
* @param {new (sku: string) => T} Kind
* @param {string[]} skus
* @returns {T[]}
*/
export function createAll(Kind, skus) {
return skus.map((sku) => new Kind(sku));
}
class Shirt {
/** @param {string} sku */
constructor(sku) {
this.sku = sku;
this.size = "M";
}
}
const shirts = createAll(Shirt, ["TEE-01", "TEE-02"]);
shirts[0].size;export function createAll<T>(Kind: new (sku: string) => T, skus: string[]): T[] {
return skus.map((sku) => new Kind(sku));
}
class Shirt {
sku: string;
size: string;
constructor(sku: string) {
this.sku = sku;
this.size = "M";
}
}
const shirts = createAll(Shirt, ["TEE-01", "TEE-02"]);
shirts[0].size;jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} export class Shirt {
/** @param {string} sku */
constructor(sku) {
this.sku = sku;
}
static category = "apparel";
}
/** @param {typeof Shirt} Kind */
export function describe(Kind) {
return `${Kind.category}: ${new Kind("TEE-01").sku}`;
}
describe(Shirt);jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /**
* @param {new (sku: string) => { sku: string }} Kind
*/
export function createOne(Kind) {
return new Kind("TEE-01");
}
/** @param {string} sku */
const makeShirt = (sku) => ({ sku });
createOne(makeShirt);error TS2345: Argument of type '(sku: string) => { sku: string; }' is not assignable to parameter of type 'new (sku: string) => { sku: string; }'.
Type '(sku: string) => { sku: string; }' provides no match for the signature 'new (sku: string): { sku: string; }'.jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Generic factories
Combine a constructor type with @template T to keep the instance type: createAll(Shirt, skus) returns Shirt[], so shirts[0].size is checked. That pattern is common for registries, dependency injection and test helpers.
Plain functions do not fit
An arrow function or a factory function cannot be called with new, so passing one where a constructor type is expected is TS2345, as the third example shows. If both should be accepted, take a function type instead and call it without new.
Gotchas
- In a
.tsfile an abstract class cannot be created withnew, so it does not fit anew (…) => Tparameter; JavaScript has no abstract classes, so this only matters for classes declared in.d.tsfiles. - The Closure form
{function(new: Shirt, string)}is read as a constructor type that takes a string and creates aShirt.
Questions
How do I type a class passed as a parameter in JSDoc?
With a constructor type, {new (...args: any[]) => T}, or with {typeof MyClass} for one specific class.
Sources
- More on Functions: construct signatures · TypeScript handbook
- Generics: using class types in generics · TypeScript handbook
- JSDoc Reference: types · TypeScript handbook
Last reviewed 20 October 2026.