JSDoc
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.
@type
ExactAnnotates 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)- Applies to variable, class-field, and type-alias positions
@param (aliases @arg, @argument)
Works, with caveatsTypes 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
}- Fills only a parameter with no inline
: Tannotation and no destructuring pattern (an inline type wins) - A
{...T}varargs type is stripped to its baseT; binding it to a body that readsargumentsstill needs the unsupportedargumentsobject — use a declared...rest: T[]parameter
@returns (alias @return)
Works, with caveatsSupplies 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
}- Fills only when the function has no inline
: RetType
@typedef
Works, with caveatsDeclares 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
}- Both forms work:
@typedef {Object} Name+@property {T} field, and the inline@typedef {{x: number}} Nameobject literal - A
@typedef {T} Namealias to a **width keyword** (int32) does not propagate the integer semantics — the same pre-existing limit a TStype X = int32alias has
@callback
ExactDeclares 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)
}- Param/return types use the full type-expression grammar
@template
Works, with caveatsThe 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]
}- 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 caveatsAccepted and erased, parity with the erased TS `satisfies` operator — the value keeps its own type and gets no conformance check.
- Accepted and erased — the value keeps its own type, no
satisfies-style excess-property/conformance check (parity with the erased TSsatisfiesoperator)
@enum
Works, with caveatsTags a `const` object for value access (`Dir.Up`). It stays a plain object, not a nominal enum type.
- The tagged
constobject 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 caveatsAccepted and erased. A `this` parameter type is not separately modeled — `this` is bound by the class method it lives in.
- Accepted and erased; a
thisparameter type is not separately modeled (thisis bound by the class method it lives in)
@extends (alias @augments)
Works, with caveatsAccepted and erased — the class's own `extends` clause does the work; the tag does not add type arguments to a generic base.
- Accepted and erased — the class's own
extendsclause does the work; the tag does not add type arguments to a generic base
@implements
Works, with caveatsAccepted and erased — like a TS `implements` clause here, it does not strictly enforce structural conformance.
- Accepted and erased — like a TS
implementsclause here, it does not strictly enforce structural conformance
@public
Works, with caveatsAccepted and erased, not enforced — the same stance as the TS `public` field modifier here.
- Accepted and erased, not enforced — the same stance as the TS
publicfield modifier here
@private
Works, with caveatsAccepted and erased, not enforced — no access checking (the TS `private` modifier is likewise erased, not enforced, here).
- Accepted and erased, not enforced — no access checking (the TS
privatemodifier is likewise erased, not enforced, here)
@protected
Works, with caveatsAccepted and erased, not enforced.
- Accepted and erased, not enforced
@readonly
Works, with caveatsAccepted and erased, not enforced — no mutation checking, parity with the erased TS `readonly` modifier.
- Accepted and erased, not enforced — no mutation checking (parity with the erased TS
readonlymodifier)
@override
Works, with caveatsAccepted and erased — no override-consistency check.
- Accepted and erased — no override-consistency check
@deprecated
Works, with caveatsDocumentation-only; accepted with no compile effect, parity with TS's runtime behavior.
- Documentation-only; accepted with no compile effect, but there is no tooling layer to surface a deprecation warning
@see
ExactDocumentation-only, no type effect — accepted, parity with TS.
@link
ExactDocumentation-only, no type effect — accepted, parity with TS.
@import
Not supportedThe 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.
- The comment-form import statement is not synthesized; use a normal
importplus animport("./m").Ttype reference (which resolves — see the type-expression table)
@constructor (alias @class)
Not supportedMarks a plain function as a constructor (legacy pre-`class` JS). Unsupported by design — this compiler uses real `class` declarations, not function-as-constructor.
- Marks a plain function as a constructor (legacy pre-
classJS) — unsupported; this compiler uses realclassdeclarations, not function-as-constructor
Bare primitive (string, number, boolean, …)
ExactResolved by the same type resolver a TS annotation uses.
T[] array
ExactThe `[]` suffix is handled by the resolver.
Optional param (T=, [name])
Works, with caveatsThe optional decoration is recognized at the `@param` name/type level and stripped.
- Recognized at the
@paramname/type level and stripped; the parameter is not yet marked structurally optional beyond an inline?
Varargs/rest (...T)
Works, with caveatsStripped 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
}- Stripped to the base
T; the varargs body needs a declared...restparameter (noarguments)
Union (A | B)
Works, with caveatsThe 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
}- Inherits the union V1 scope (scalar/object/
ReadableStreammembers, narrowing rules — see the Type system page)
Nullable (?T)
Works, with caveatsA leading `?` marker becomes a union with null (`?number` → `number | null`).
- The leading marker (
?number→number | null); a?buried mid-expression is left to the parser
Non-null (!T)
Works, with caveatsThe marker is stripped (`!number` → `number`) — TS treats non-null as no semantic change.
- The marker is stripped (
!number→number) — TS treats non-null as no semantic change
Object shape ({ a: string, b: number })
ExactInline 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
}- Nested braces are supported (
@param {{x: number}}) via balanced-brace scanning
Array.<T> / Array<T> (generic array form)
ExactThe Closure dot form (`Array.<T>`) is normalized to `Array<T>`.
Object.<K, V> (index map)
supportedNormalized 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 caveatsRewritten to the arrow form `(arg0: A) => B`.
/** @param {function(number): number} f */
function twice(f) {
return f(f(3))
}- Rewritten to the arrow form
(arg0: A) => B; a nestedfunction(...)inside another type is not rewritten (rare)
* / ? (Closure any/unknown)
ExactBoth are normalized to `any`.
import("./m").T type
Works, with caveatsThe `import("./m").Name` qualifier is dropped to the bare `Name`, which resolves under whole-program compilation when that module is part of the build.
- The
import("./m").Namequalifier is dropped to the bareName, which resolves under whole-program compilation when that module is part of the build (typically because the file also imports from it) — aNamenot otherwise pulled in won't resolve; thetypeof import(...)value form is out of scope
@typedef/@callback-defined names
ExactNames introduced by `@typedef`/`@callback` resolve wherever a type name does; inline `{{…}}` typedefs work too.
Legacy synonyms (String→string, …)
Works, with caveatsThe capitalized Closure-era synonyms remap to their lowercase primitives.
String/Number/Boolean/Void/Undefined/Nullremap to the lowercase primitive; bareObject/objectare left as-is
Integer width keywords (@type {int8..int64, uint8..uint64})
ExactCompiler extensionA 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)Extended float keywords (@type {float32, float64})
ExactCompiler extensionA 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)@erased (generic single-body compilation)
ExactCompiler extensionA 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 extensionA 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 }