API reference

JSDoc

35/37Coverage9/37Strict (zero caveats)

JSDoc here is a real typing surface, not decoration: the type body of a tag is parsed by the same type parser an inline annotation uses, so a comment can supply a type where nothing else does. The recurring divergence is deliberate erasure — the class/access tags (@readonly, @private, @override, @satisfies, @enum, @implements) are accepted and stripped without enforcement, matching how this compiler already erases the inline TS keywords they mirror; enforcing only the doc-comment spelling would be backwards. @param/@returns/@typedef/@template do the load-bearing work but never override an inline type, and two legacy function-as-constructor tags (@import, @constructor) are out of scope in favor of real class declarations.

Exact Works, with caveats Not supported

@type

Exact
/** @type {T} */ let x = value

Annotates a variable, class field, or type-alias position. The value string runs through the real type parser, so the whole type-expression grammar below is available inside it — including this compiler's integer width keywords.

/** @type {uint8} */
let r = 255
r = r + 1
console.log(r)                    // 0  (wrapped at 8 bits)
See it in a full example →
⚠ Differences from standard
  • Applies to variable, class-field, and type-alias positions

@param (aliases @arg, @argument)

Works, with caveats
/** @param {T} name */

Types a parameter from the doc comment — the core of the typed-JavaScript workflow. It fills in only where the parameter has no inline annotation, so an existing `: T` always wins. The type body uses the full grammar below, and a `{number=}` or `[name]` optional decoration is recognized and stripped.

/**
 * @param {int32} a
 * @param {int32} b
 * @returns {int32}
 */
function idiv(a, b) {
  return a / b
}
See it in a full example →
⚠ Differences from standard
  • Fills only a parameter with no inline : T annotation and no destructuring pattern (an inline type wins)
  • A {...T} varargs type is stripped to its base T; binding it to a body that reads arguments still needs the unsupported arguments object — use a declared ...rest: T[] parameter

@returns (alias @return)

Works, with caveats
/** @returns {T} */

Supplies a function's return type from the doc comment, filling in only when there is no inline `: RetType`.

/**
 * @param {string} s
 * @returns {number} the code unit count
 */
function width(s) {
  return s.length
}
See it in a full example →
⚠ Differences from standard
  • Fills only when the function has no inline : RetType

@typedef

Works, with caveats
/** @typedef {Object} Name */ + @property, or @typedef {{x: number}} Name

Declares a named type. Both spellings work — the `@typedef {Object} Name` + `@property {T} field` form and the inline `@typedef {{x: number}} Name` object literal — and the result is synthesized into a `type Name = …` declaration, so it resolves anywhere a type name does.

/**
 * @typedef {Object} Point
 * @property {number} x
 * @property {number} y
 */

/** @param {Point} p */
function manhattan(p) {
  return p.x + p.y
}
See it in a full example →
⚠ Differences from standard
  • Both forms work: @typedef {Object} Name + @property {T} field, and the inline @typedef {{x: number}} Name object literal
  • A @typedef {T} Name alias to a **width keyword** (int32) does not propagate the integer semantics — the same pre-existing limit a TS type X = int32 alias has

@callback

Exact
/** @callback Name */ + @param / @returns

Declares a named function type from its `@param`/`@returns` tags, synthesized into a function-type alias that is usable wherever a type name is.

/**
 * @callback Combine
 * @param {number} a
 * @param {number} b
 * @returns {number}
 */

/** @param {Combine} fn */
function run(fn) {
  return fn(10, 5)
}
See it in a full example →
⚠ Differences from standard
  • Param/return types use the full type-expression grammar

@template

Works, with caveats
/** @template T */

The JSDoc form of a `<T>` generic — it sets the function's type-parameter list and drives the exact same monomorphization a TS `<T>` does. `@erased` is the compile-once variant.

/**
 * @template T
 * @param {T[]} arr
 * @returns {T}
 */
function firstOf(arr) {
  return arr[0]
}
See it in a full example →
⚠ Differences from standard
  • Inherits the TS generics scope: V1 monomorphization needs an inferable T/T[]-typed parameter, no explicit call-site type arguments; a {Base} constraint on a multi-name tag applies to the first name (matching TS)

@satisfies

Works, with caveats
/** @satisfies {T} */

Accepted and erased, parity with the erased TS `satisfies` operator — the value keeps its own type and gets no conformance check.

⚠ Differences from standard
  • Accepted and erased — the value keeps its own type, no satisfies-style excess-property/conformance check (parity with the erased TS satisfies operator)

@enum

Works, with caveats
/** @enum {T} */ const Dir = { ... }

Tags a `const` object for value access (`Dir.Up`). It stays a plain object, not a nominal enum type.

⚠ Differences from standard
  • The tagged const object works for value access (Dir.Up); it is a plain object, not a nominal enum type — no reverse mapping, no enum-member type narrowing

@this

Works, with caveats
/** @this {T} */

Accepted and erased. A `this` parameter type is not separately modeled — `this` is bound by the class method it lives in.

⚠ Differences from standard
  • Accepted and erased; a this parameter type is not separately modeled (this is bound by the class method it lives in)

@extends (alias @augments)

Works, with caveats
/** @extends {Base} */

Accepted and erased — the class's own `extends` clause does the work; the tag does not add type arguments to a generic base.

⚠ Differences from standard
  • Accepted and erased — the class's own extends clause does the work; the tag does not add type arguments to a generic base

@implements

Works, with caveats
/** @implements {Iface} */

Accepted and erased — like a TS `implements` clause here, it does not strictly enforce structural conformance.

⚠ Differences from standard
  • Accepted and erased — like a TS implements clause here, it does not strictly enforce structural conformance

@public

Works, with caveats
/** @public */

Accepted and erased, not enforced — the same stance as the TS `public` field modifier here.

⚠ Differences from standard
  • Accepted and erased, not enforced — the same stance as the TS public field modifier here

@private

Works, with caveats
/** @private */

Accepted and erased, not enforced — no access checking (the TS `private` modifier is likewise erased, not enforced, here).

⚠ Differences from standard
  • Accepted and erased, not enforced — no access checking (the TS private modifier is likewise erased, not enforced, here)

@protected

Works, with caveats
/** @protected */

Accepted and erased, not enforced.

⚠ Differences from standard
  • Accepted and erased, not enforced

@readonly

Works, with caveats
/** @readonly */

Accepted and erased, not enforced — no mutation checking, parity with the erased TS `readonly` modifier.

⚠ Differences from standard
  • Accepted and erased, not enforced — no mutation checking (parity with the erased TS readonly modifier)

@override

Works, with caveats
/** @override */

Accepted and erased — no override-consistency check.

⚠ Differences from standard
  • Accepted and erased — no override-consistency check

@deprecated

Works, with caveats
/** @deprecated */

Documentation-only; accepted with no compile effect, parity with TS's runtime behavior.

⚠ Differences from standard
  • Documentation-only; accepted with no compile effect, but there is no tooling layer to surface a deprecation warning

@see

Exact
/** @see reference */

Documentation-only, no type effect — accepted, parity with TS.

@author

Exact
/** @author name */

Documentation-only, no type effect — accepted, parity with TS.

@import

Not supported
// not supported

The comment-form import statement is not synthesized. Use a normal `import` plus an `import("./m").T` type reference, which does resolve — see the type-expression table.

⚠ Differences from standard
  • The comment-form import statement is not synthesized; use a normal import plus an import("./m").T type reference (which resolves — see the type-expression table)

@constructor (alias @class)

Not supported
// not supported

Marks a plain function as a constructor (legacy pre-`class` JS). Unsupported by design — this compiler uses real `class` declarations, not function-as-constructor.

⚠ Differences from standard
  • Marks a plain function as a constructor (legacy pre-class JS) — unsupported; this compiler uses real class declarations, not function-as-constructor

Bare primitive (string, number, boolean, …)

Exact
{string} | {number} | {boolean} | …

Resolved by the same type resolver a TS annotation uses.

T[] array

Exact
{T[]}

The `[]` suffix is handled by the resolver.

Optional param (T=, [name])

Works, with caveats
{T=} | @param {T} [name]

The optional decoration is recognized at the `@param` name/type level and stripped.

⚠ Differences from standard
  • Recognized at the @param name/type level and stripped; the parameter is not yet marked structurally optional beyond an inline ?

Varargs/rest (...T)

Works, with caveats
{...T}

Stripped to the base `T`; the varargs value flows into a declared `...rest` parameter.

/**
 * @param {...number} nums
 */
function total(nums: number[]) {
  let t = 0
  for (const n of nums) t += n
  return t
}
See it in a full example →
⚠ Differences from standard
  • Stripped to the base T; the varargs body needs a declared ...rest parameter (no arguments)

Union (A | B)

Works, with caveats
{A | B}

The JSDoc string is parsed by the real type parser, so a union resolves like an inline one.

/** @param {number | string} v */
function kind(v) {
  return typeof v
}
See it in a full example →
⚠ Differences from standard
  • Inherits the union V1 scope (scalar/object/ReadableStream members, narrowing rules — see the Type system page)

Nullable (?T)

Works, with caveats
{?T}

A leading `?` marker becomes a union with null (`?number` → `number | null`).

⚠ Differences from standard
  • The leading marker (?number → number | null); a ? buried mid-expression is left to the parser

Non-null (!T)

Works, with caveats
{!T}

The marker is stripped (`!number` → `number`) — TS treats non-null as no semantic change.

⚠ Differences from standard
  • The marker is stripped (!number → number) — TS treats non-null as no semantic change

Object shape ({ a: string, b: number })

Exact
{{ a: string, b: number }}

Inline object shapes resolve, and nested braces are supported (`@param {{x: number}}`) via balanced-brace scanning.

/** @param {{name: string, age: number}} u */
function greet(u) {
  return u.name
}
See it in a full example →
⚠ Differences from standard
  • Nested braces are supported (@param {{x: number}}) via balanced-brace scanning

Array.<T> / Array<T> (generic array form)

Exact
{Array.<T>} | {Array<T>}

The Closure dot form (`Array.<T>`) is normalized to `Array<T>`.

Object.<K, V> (index map)

supported
{Object.<K, V>}

Normalized to `Record<K, V>`, backed by real index-signature support — per-key read/write (`d[key]`) works, e.g. `@type {Object.<string, number>}` on a dictionary object.

Function type (function(A): B)

Works, with caveats
{function(A): B}

Rewritten to the arrow form `(arg0: A) => B`.

/** @param {function(number): number} f */
function twice(f) {
  return f(f(3))
}
See it in a full example →
⚠ Differences from standard
  • Rewritten to the arrow form (arg0: A) => B; a nested function(...) inside another type is not rewritten (rare)

* / ? (Closure any/unknown)

Exact
{*} | {?}

Both are normalized to `any`.

import("./m").T type

Works, with caveats
{import("./m").T}

The `import("./m").Name` qualifier is dropped to the bare `Name`, which resolves under whole-program compilation when that module is part of the build.

⚠ Differences from standard
  • The import("./m").Name qualifier is dropped to the bare Name, which resolves under whole-program compilation when that module is part of the build (typically because the file also imports from it) — a Name not otherwise pulled in won't resolve; the typeof import(...) value form is out of scope

@typedef/@callback-defined names

Exact
{MyTypedef} | {MyCallback}

Names introduced by `@typedef`/`@callback` resolve wherever a type name does; inline `{{…}}` typedefs work too.

Legacy synonyms (String→string, …)

Works, with caveats
{String} → {string}, …

The capitalized Closure-era synonyms remap to their lowercase primitives.

⚠ Differences from standard
  • String/Number/Boolean/Void/Undefined/Null remap to the lowercase primitive; bare Object/object are left as-is

Integer width keywords (@type {int8..int64, uint8..uint64})

ExactCompiler extension
/** @type {int32} */ | /** @param {uint8} x */

A compiler extension with no TypeScript equivalent: the real-integer escape hatch — exact semantics and bit-width storage, also usable in `@param`/`@returns`.

/** @type {int64} */
let bigExact = 9007199254740993
console.log(bigExact)             // 9007199254740993  (exact, via int64)
See it in a full example →

Extended float keywords (@type {float32, float64})

ExactCompiler extension
/** @type {float32} */ | /** @type {float64} */

A compiler extension with no TypeScript equivalent: `float64` is the default `number`; `float32` is a narrower slot.

/** @type {float32} */
let ratio = 1 / 3
console.log(ratio)                // 0.3333333432674408  (float32 rounding)
See it in a full example →

@erased (generic single-body compilation)

ExactCompiler extension
/** @erased */

A compiler extension with no TypeScript equivalent: opts a generic function out of monomorphization — the JSDoc analogue of a `<T>` the compiler compiles once.

@pure (enforced side-effect-free function)

ExactCompiler extension
/** @pure */

A compiler extension: asserts a function is side-effect-free and referentially transparent, and ENFORCES it with a front-end pass — zero runtime/codegen cost, output byte-identical to the untagged function. Inside a @pure function (declaration, arrow binding, or function-expression binding, including capturing closures) the compiler rejects the five impurity classes: mutating a parameter (or anything reachable from it), mutating a captured/global binding, I/O (console/fs/fetch/…), nondeterminism (Math.random/Date.now/new Date()), and calling a function that is not itself @pure (purity is contagious). Local mutation is allowed — only observable effects are constrained.

/** @pure */
function sq(n: number): number { return n * n }